Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ jobs:
poetry install
- run: make -C docs html

- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: DocumentationHTML
path: docs/build/html/
Expand Down
14 changes: 7 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# ================= BUILD BACKEND ==================
FROM python:3.12-alpine AS backend-builder
FROM python:3.12-slim AS backend-builder

# Install the build system
RUN apk add --update git
RUN apt-get update && apt-get install -y git
RUN python -m pip install -U pip wheel
RUN python -m pip install build

Expand All @@ -14,10 +14,10 @@ WORKDIR /build
RUN python -m build --wheel .

# ================= BUILD FRONTEND ==================
FROM node:lts-alpine as frontend-builder
FROM node:lts-alpine AS frontend-builder

# Copy the source code
ENV PATH /app/node_modules/.bin:$PATH
ENV PATH=/app/node_modules/.bin:$PATH
COPY ./ui /app

# Build the application
Expand All @@ -26,14 +26,14 @@ RUN npm install
RUN npm run build

# =============== PRODUCTION ===============
FROM python:3.12-alpine
FROM python:3.12-slim

# Install the application
RUN apk add --update git gcc musl-dev bash
RUN apt-get update && apt-get install -y git bash

# Copy frontend build artifact
COPY --from=frontend-builder /app/dist/ /ui/
ENV FOXOPS_FRONTEND_DIST_DIR /ui
ENV FOXOPS_FRONTEND_DIST_DIR=/ui

# Copy backend build artifact
COPY --from=backend-builder /build/dist/*.whl /tmp
Expand Down
128 changes: 113 additions & 15 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ readme = "README.md"
fengine = 'foxops.engine.__main__:app'

[tool.poetry.dependencies]
python = ">=3.12,<4.0"
python = ">=3.12,<3.13"

# Web Framework
uvicorn = "^0.31.0"
Expand Down
Empty file.
23 changes: 23 additions & 0 deletions src/foxops/database/repositories/change/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from foxops.errors import FoxopsError


class ChangeConflictError(FoxopsError):
def __init__(self, incarnation_id: int, revision: int) -> None:
super().__init__(f"Change with revision {revision} already exists for incarnation {incarnation_id}")


class ChangeNotFoundError(FoxopsError):
def __init__(self, id_: int) -> None:
super().__init__(f"Change with id {id_} not found")


class ChangeCommitAlreadyPushedError(FoxopsError):
def __init__(self, id_: int) -> None:
super().__init__(
f"The commit for change with id {id_} was already pushed. Then the commit sha cannot be changed."
)


class IncarnationHasNoChangesError(FoxopsError):
def __init__(self, incarnation_id: int) -> None:
super().__init__(f"Incarnation with id {incarnation_id} has no changes")
58 changes: 58 additions & 0 deletions src/foxops/database/repositories/change/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import enum
from datetime import datetime, timezone
from typing import Self

from pydantic import BaseModel, ConfigDict


class ChangeType(enum.Enum):
DIRECT = "direct"
MERGE_REQUEST = "merge_request"


class ChangeInDB(BaseModel):
id: int

incarnation_id: int
revision: int

commit_sha: str
commit_pushed: bool

type: ChangeType
created_at: datetime

requested_version_hash: str
requested_version: str
requested_data: str

template_data_full: str

merge_request_id: str | None
merge_request_branch_name: str | None
model_config = ConfigDict(from_attributes=True)

@classmethod
def from_database_row(cls, obj) -> Self:
change_in_db = cls.model_validate(obj)
change_in_db.created_at = change_in_db.created_at.replace(tzinfo=timezone.utc)

return change_in_db


class IncarnationWithChangesSummary(BaseModel):
"""Represents an incarnation combined with information about its latest change."""

id: int

incarnation_repository: str
target_directory: str
template_repository: str

revision: int
type: ChangeType
commit_sha: str
requested_version: str
merge_request_id: str | None
created_at: datetime
model_config = ConfigDict(from_attributes=True)
Original file line number Diff line number Diff line change
@@ -1,92 +1,26 @@
import enum
from datetime import datetime, timezone
from typing import AsyncIterator, Self
from typing import AsyncIterator

from pydantic import BaseModel, ConfigDict
from sqlalchemy import and_, delete, desc, insert, select, update
from sqlalchemy.exc import IntegrityError, NoResultFound
from sqlalchemy.ext.asyncio import AsyncEngine

from foxops.database.repositories.change.errors import (
ChangeCommitAlreadyPushedError,
ChangeConflictError,
ChangeNotFoundError,
IncarnationHasNoChangesError,
)
from foxops.database.repositories.change.model import (
ChangeInDB,
ChangeType,
IncarnationWithChangesSummary,
)
from foxops.database.schema import change, incarnations
from foxops.errors import FoxopsError, IncarnationNotFoundError
from foxops.errors import IncarnationNotFoundError
from foxops.logger import get_logger


class ChangeConflictError(FoxopsError):
def __init__(self, incarnation_id: int, revision: int) -> None:
super().__init__(f"Change with revision {revision} already exists for incarnation {incarnation_id}")


class ChangeNotFoundError(FoxopsError):
def __init__(self, id_: int) -> None:
super().__init__(f"Change with id {id_} not found")


class ChangeCommitAlreadyPushedError(FoxopsError):
def __init__(self, id_: int) -> None:
super().__init__(
f"The commit for change with id {id_} was already pushed. Then the commit sha cannot be changed."
)


class IncarnationHasNoChangesError(FoxopsError):
def __init__(self, incarnation_id: int) -> None:
super().__init__(f"Incarnation with id {incarnation_id} has no changes")


class ChangeType(enum.Enum):
DIRECT = "direct"
MERGE_REQUEST = "merge_request"


class ChangeInDB(BaseModel):
id: int

incarnation_id: int
revision: int

commit_sha: str
commit_pushed: bool

type: ChangeType
created_at: datetime

requested_version_hash: str
requested_version: str
requested_data: str

template_data_full: str

merge_request_id: str | None
merge_request_branch_name: str | None
model_config = ConfigDict(from_attributes=True)

@classmethod
def from_database_row(cls, obj) -> Self:
change_in_db = cls.model_validate(obj)
change_in_db.created_at = change_in_db.created_at.replace(tzinfo=timezone.utc)

return change_in_db


class IncarnationWithChangesSummary(BaseModel):
"""Represents an incarnation combined with information about its latest change."""

id: int

incarnation_repository: str
target_directory: str
template_repository: str

revision: int
type: ChangeType
commit_sha: str
requested_version: str
merge_request_id: str | None
created_at: datetime
model_config = ConfigDict(from_attributes=True)


class ChangeRepository:
def __init__(self, engine: AsyncEngine) -> None:
self.engine = engine
Expand Down
2 changes: 1 addition & 1 deletion src/foxops/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sqlalchemy.ext.asyncio import AsyncEngine

from foxops.database.engine import create_engine
from foxops.database.repositories.change import ChangeRepository
from foxops.database.repositories.change.repository import ChangeRepository
from foxops.database.repositories.incarnation.repository import IncarnationRepository
from foxops.hosters import Hoster
from foxops.hosters.gitlab import GitlabHoster
Expand Down
4 changes: 2 additions & 2 deletions src/foxops/routers/changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from fastapi import APIRouter, Depends, HTTPException, Path, status
from pydantic import BaseModel

from foxops.database.repositories.change import ChangeNotFoundError
from foxops.database.repositories.change import ChangeType as DatabaseChangeType
from foxops.database.repositories.change.errors import ChangeNotFoundError
from foxops.database.repositories.change.model import ChangeType as DatabaseChangeType
from foxops.dependencies import get_change_service
from foxops.engine import TemplateData
from foxops.hosters.types import MergeRequestStatus
Expand Down
4 changes: 2 additions & 2 deletions src/foxops/services/change.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
from pydantic import BaseModel

import foxops.engine as fengine
from foxops.database.repositories.change import (
ChangeRepository,
from foxops.database.repositories.change.model import (
ChangeType,
IncarnationWithChangesSummary,
)
from foxops.database.repositories.change.repository import ChangeRepository
from foxops.database.repositories.incarnation.repository import IncarnationRepository
from foxops.engine import TemplateData
from foxops.engine.patching.git_diff_patch import PatchResult
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from foxops.__main__ import create_app
from foxops.database.engine import create_engine
from foxops.database.repositories.change import ChangeRepository
from foxops.database.repositories.change.repository import ChangeRepository
from foxops.database.repositories.incarnation.repository import IncarnationRepository
from foxops.database.schema import meta
from foxops.dependencies import (
Expand Down
2 changes: 1 addition & 1 deletion tests/database/migrations/test_00ee97d0b7a3.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from alembic.command import upgrade
from alembic.script import ScriptDirectory
from foxops.database.repositories.change import ChangeRepository
from foxops.database.repositories.change.repository import ChangeRepository

INSERT_INCARNAION = text(
"""
Expand Down
6 changes: 3 additions & 3 deletions tests/database/test_change_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
import pytest
from pytest import fixture

from foxops.database.repositories.change import (
from foxops.database.repositories.change.errors import (
ChangeCommitAlreadyPushedError,
ChangeConflictError,
ChangeNotFoundError,
ChangeRepository,
ChangeType,
IncarnationHasNoChangesError,
)
from foxops.database.repositories.change.model import ChangeType
from foxops.database.repositories.change.repository import ChangeRepository
from foxops.database.repositories.incarnation.model import IncarnationInDB
from foxops.database.repositories.incarnation.repository import IncarnationRepository

Expand Down
2 changes: 1 addition & 1 deletion tests/database/test_incarnation_repository.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from pytest import fixture, raises

from foxops.database.repositories.change import ChangeRepository
from foxops.database.repositories.change.repository import ChangeRepository
from foxops.database.repositories.incarnation.errors import (
IncarnationAlreadyExistsError,
IncarnationNotFoundError,
Expand Down
2 changes: 1 addition & 1 deletion tests/routers/test_incarnations.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from httpx import AsyncClient
from pytest_mock import MockFixture

from foxops.database.repositories.change import ChangeRepository
from foxops.database.repositories.change.repository import ChangeRepository
from foxops.database.repositories.incarnation.repository import IncarnationRepository
from foxops.dependencies import get_change_service
from foxops.models.change import Change
Expand Down
3 changes: 2 additions & 1 deletion tests/services/test_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from pytest import fixture
from sqlalchemy.ext.asyncio import AsyncEngine

from foxops.database.repositories.change import ChangeNotFoundError, ChangeRepository
from foxops.database.repositories.change.errors import ChangeNotFoundError
from foxops.database.repositories.change.repository import ChangeRepository
from foxops.database.repositories.incarnation.errors import IncarnationNotFoundError
from foxops.database.repositories.incarnation.repository import IncarnationRepository
from foxops.engine import IncarnationState
Expand Down