debate/backend/app/db/models/build.py
Mikkel Georgsen c261664784 feat(01-02): configure Alembic and create Build model
- Configure Alembic for async migrations with SQLAlchemy 2.0
- Create Build model with UUID primary key, config_hash, status enum
- Add indexes on status (queue queries) and config_hash (cache lookups)
- Generate and apply initial migration creating builds table

Build model fields: id, config_hash, status, iso_path, error_message,
build_log, started_at, completed_at, created_at, updated_at.
2026-01-25 20:11:55 +00:00

113 lines
3 KiB
Python

"""Build tracking model for ISO generation."""
import enum
import uuid
from datetime import datetime
from sqlalchemy import DateTime, Enum, Index, String, Text, func
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column
from backend.app.db.base import Base
class BuildStatus(enum.Enum):
"""Status values for build tracking."""
PENDING = "pending"
BUILDING = "building"
COMPLETED = "completed"
FAILED = "failed"
CACHED = "cached"
class Build(Base):
"""Model for tracking ISO build jobs.
Attributes:
id: Unique identifier for the build (UUID)
config_hash: SHA-256 hash of the build configuration (64 chars)
status: Current build status
iso_path: Path to generated ISO file (if completed)
error_message: Error message if build failed
build_log: Full build output log
started_at: Timestamp when build started
completed_at: Timestamp when build completed
created_at: Timestamp when build was created
updated_at: Timestamp of last update
The config_hash enables caching - identical configurations can
return existing ISOs without rebuilding.
"""
__tablename__ = "builds"
# Primary key
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
primary_key=True,
default=uuid.uuid4,
)
# Configuration hash for caching (SHA-256 = 64 hex chars)
config_hash: Mapped[str] = mapped_column(
String(64),
unique=True,
index=True,
nullable=False,
)
# Build status
status: Mapped[BuildStatus] = mapped_column(
Enum(BuildStatus),
default=BuildStatus.PENDING,
nullable=False,
)
# Build results
iso_path: Mapped[str | None] = mapped_column(
String(512),
nullable=True,
)
error_message: Mapped[str | None] = mapped_column(
Text,
nullable=True,
)
build_log: Mapped[str | None] = mapped_column(
Text,
nullable=True,
)
# Timing
started_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True),
nullable=True,
)
completed_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True),
nullable=True,
)
# Audit timestamps
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
nullable=False,
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
onupdate=func.now(),
nullable=False,
)
# Indexes for common queries
__table_args__ = (
# Index on status for queue queries (find pending builds)
Index("ix_builds_status", "status"),
# Index on config_hash already created via column definition
)
def __repr__(self) -> str:
"""String representation of Build."""
return f"<Build {self.id} status={self.status.value}>"