Backup/Restore Regression Test #167
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Backup/Restore Regression Test | |
| # This does not back up a deployed environment. It builds synthetic DuckDB | |
| # fixtures on an ephemeral runner, backs *those* up, verifies the manifest, | |
| # and restores into a scratch directory — a regression test for | |
| # scripts/backup.py, scripts/verify_backup.py and scripts/restore.py, not a | |
| # disaster-recovery control (audit P1-2). See docs/operations/disaster-recovery.md for | |
| # what backup coverage actually exists today. | |
| on: | |
| schedule: | |
| - cron: "0 2 * * *" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| backup: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.11" | |
| - name: Install dependencies | |
| run: bash scripts/ci_sync.sh dev-tools | |
| - name: Prepare backup fixture | |
| run: | | |
| python - <<'PY' | |
| from pathlib import Path | |
| import duckdb | |
| from agentflow_runtime.serving.backends.duckdb_backend import DuckDBBackend | |
| pipeline_db = Path("agentflow_demo.duckdb") | |
| usage_db = Path("agentflow_api.duckdb") | |
| for path in (pipeline_db, usage_db): | |
| if path.exists(): | |
| path.unlink() | |
| wal_path = Path(f"{path}.wal") | |
| if wal_path.exists(): | |
| wal_path.unlink() | |
| # The product's own DDL, not a copy of it. This step used to hand-write | |
| # a `CREATE SCHEMA` per tenant, parsed out of the `duckdb_schema` field | |
| # of config/tenants.yaml -- the schema-per-tenant model that ADR-004 | |
| # retired. Nothing in src/ ever created those schemas, so the fixture | |
| # was fabricating the very thing the restore smoke test then went | |
| # looking for. Two tenants now share one table, keyed by `tenant_id`, | |
| # which is what a restored store actually has to bring back. | |
| backend = DuckDBBackend(db_path=str(pipeline_db)) | |
| backend.ensure_schema() | |
| conn = backend.connection | |
| try: | |
| conn.execute( | |
| """ | |
| INSERT OR REPLACE INTO orders_v2 | |
| (tenant_id, order_id, user_id, status, total_amount, currency, created_at) | |
| VALUES | |
| ('acme-corp', 'ORD-ACME-001', 'USR-ACME', 'confirmed', 42.00, 'RUB', | |
| CURRENT_TIMESTAMP), | |
| ('demo', 'ORD-DEMO-001', 'USR-DEMO', 'confirmed', 17.00, 'RUB', | |
| CURRENT_TIMESTAMP) | |
| """ | |
| ) | |
| finally: | |
| conn.close() | |
| conn = duckdb.connect(str(usage_db)) | |
| try: | |
| conn.execute( | |
| """ | |
| CREATE TABLE IF NOT EXISTS api_usage ( | |
| tenant TEXT, | |
| key_name TEXT, | |
| endpoint TEXT, | |
| ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP | |
| ) | |
| """ | |
| ) | |
| conn.execute( | |
| """ | |
| INSERT INTO api_usage (tenant, key_name, endpoint) | |
| VALUES ('demo', 'backup-workflow', '/v1/health') | |
| """ | |
| ) | |
| finally: | |
| conn.close() | |
| PY | |
| - name: Create backup archive (synthetic fixture) | |
| run: python scripts/backup.py --output .artifacts/backup-regression | |
| - name: Resolve backup path | |
| id: backup | |
| shell: bash | |
| run: | | |
| archive_path="$(ls -1t .artifacts/backup-regression/*.tar.gz | head -n 1)" | |
| echo "archive_path=$archive_path" >> "$GITHUB_OUTPUT" | |
| - name: Verify backup manifest | |
| run: python scripts/verify_backup.py "${{ steps.backup.outputs.archive_path }}" | |
| - name: Restore smoke test | |
| run: | | |
| python scripts/restore.py \ | |
| --backup "${{ steps.backup.outputs.archive_path }}" \ | |
| --target-root /tmp/agentflow-restore | |
| - name: Upload regression-test archive | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: agentflow-backup-restore-regression-fixture | |
| path: ${{ steps.backup.outputs.archive_path }} | |
| retention-days: 7 |