fix: Create fresh DB template and atomically swap it

- Unit tests tend to fail due inconsitent DB file.
- Create a fresh copy on a temporary location and the atomically swap it.
- To always provide a completely initialized and migrated DB.
This commit is contained in:
CoreRasurae
2026-04-23 23:07:33 +01:00
committed by Forgejo
parent 65f1c40533
commit d4d43862e3
+23 -4
View File
@@ -36,11 +36,26 @@ def create_template(output_path: str = "build/.template-migrated.db") -> None:
out = Path(output_path)
out.parent.mkdir(parents=True, exist_ok=True)
# Remove existing template so we always create fresh
if out.exists():
out.unlink()
# Remove existing template and any SQLite journal/WAL files so we always
# create fresh. SQLite may leave behind -wal/-shm files that prevent
# clean recreation.
for suffix in ("", "-wal", "-shm", "-journal"):
candidate = out.with_name(out.name + suffix)
if candidate.exists():
candidate.unlink(missing_ok=True)
db_url = f"sqlite:///{out.resolve()}"
# Use a temporary file path to avoid race conditions with parallel
# workers that may still be reading the old template. We create the
# database at a unique temp path, then atomically rename it into place.
import tempfile
fd, tmp_path = tempfile.mkstemp(
dir=out.parent, prefix=f".{out.name}.tmp.", suffix=".db"
)
os.close(fd)
tmp = Path(tmp_path)
db_url = f"sqlite:///{tmp.resolve()}"
engine = create_engine(db_url, connect_args={"check_same_thread": False})
# Create all 33 tables in one fast DDL batch
@@ -71,6 +86,10 @@ def create_template(output_path: str = "build/.template-migrated.db") -> None:
engine.dispose()
# Atomically move the temp database into place. This ensures parallel
# workers never see a partially-written template.
tmp.replace(out)
# Ensure the template database is writable (0o664 = rw-rw-r--)
# This prevents sqlite3.OperationalError: attempt to write a readonly database
# when tests copy and modify the template during test setup.