feat(ops): 添加生产备份恢复脚本
- 新增 PostgreSQL 备份、fresh DB 恢复和 server_data volume 备份脚本\n- 恢复默认拒绝覆盖,必须显式 --confirm-overwrite\n- 补充 package scripts、deploy verify 校验和部署文档\n\nCo-Authored-By: GPT-5 Codex <codex@openai.com>
This commit is contained in:
85
scripts/ops-scripts.test.mjs
Normal file
85
scripts/ops-scripts.test.mjs
Normal file
@@ -0,0 +1,85 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { mkdtempSync, writeFileSync, existsSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { describe, it } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
const root = resolve(import.meta.dirname, '..');
|
||||
|
||||
function runScript(script, args) {
|
||||
return spawnSync(process.execPath, [join(root, script), ...args], {
|
||||
cwd: root,
|
||||
encoding: 'utf8',
|
||||
});
|
||||
}
|
||||
|
||||
describe('production ops scripts', () => {
|
||||
it('prints a pg_dump command in dry-run mode without writing the target file', () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'ftb-pg-backup-'));
|
||||
const output = join(dir, 'backup.dump');
|
||||
|
||||
const result = runScript('scripts/backup-postgres.mjs', [
|
||||
'--dry-run',
|
||||
'--env-file',
|
||||
'.env.production.example',
|
||||
'--output',
|
||||
output,
|
||||
]);
|
||||
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
assert.match(result.stdout, /docker compose/);
|
||||
assert.match(result.stdout, /pg_dump/);
|
||||
assert.match(result.stdout, /backup\.dump/);
|
||||
assert.equal(existsSync(output), false);
|
||||
});
|
||||
|
||||
it('refuses restore by default unless --confirm-overwrite is supplied', () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'ftb-pg-restore-'));
|
||||
const input = join(dir, 'backup.dump');
|
||||
writeFileSync(input, 'not-a-real-dump');
|
||||
|
||||
const result = runScript('scripts/restore-postgres.mjs', [
|
||||
'--env-file',
|
||||
'.env.production.example',
|
||||
'--input',
|
||||
input,
|
||||
]);
|
||||
|
||||
assert.notEqual(result.status, 0);
|
||||
assert.match(result.stderr, /--confirm-overwrite/);
|
||||
});
|
||||
|
||||
it('prints fresh database restore steps in dry-run mode after explicit overwrite confirmation', () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'ftb-pg-restore-dry-'));
|
||||
const input = join(dir, 'backup.dump');
|
||||
writeFileSync(input, 'not-a-real-dump');
|
||||
|
||||
const result = runScript('scripts/restore-postgres.mjs', [
|
||||
'--dry-run',
|
||||
'--confirm-overwrite',
|
||||
'--env-file',
|
||||
'.env.production.example',
|
||||
'--input',
|
||||
input,
|
||||
]);
|
||||
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
assert.match(result.stdout, /dropdb/);
|
||||
assert.match(result.stdout, /createdb/);
|
||||
assert.match(result.stdout, /pg_restore/);
|
||||
});
|
||||
|
||||
it('prints a server_data volume tar backup command in dry-run mode', () => {
|
||||
const result = runScript('scripts/backup-server-data.mjs', [
|
||||
'--dry-run',
|
||||
'--env-file',
|
||||
'.env.production.example',
|
||||
]);
|
||||
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
assert.match(result.stdout, /docker run/);
|
||||
assert.match(result.stdout, /ftb_pm_server_data/);
|
||||
assert.match(result.stdout, /tar -czf/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user