Skip to content

Commit cb3f2e3

Browse files
authored
feat: misc changes for quicker build and deploy times (#236)
* feat: misc changes for quicker build and dpeloy times * fix: fixture for user in frontend/unit tests * fix: deploy issue * fix: quicker frontend/unit tests - independent unit tests * feat: pulling store refactor for specific components * fix: in e2e-ready - pre-pull of python3.11 image * fix: timeout check for liveness of kube in e2e-ready * fix: event waiter * fix: teardown improvement * fix: removed xx.aclose in tests * feat: deps groups in backend * feat: conftest better fix
1 parent d8ac4cc commit cb3f2e3

87 files changed

Lines changed: 2909 additions & 1624 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/actions/e2e-ready/action.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ runs:
2020
sudo k3s kubectl config view --raw > /home/runner/.kube/config
2121
sudo chmod 600 /home/runner/.kube/config
2222
export KUBECONFIG=/home/runner/.kube/config
23-
timeout 90 bash -c 'until kubectl cluster-info 2>/dev/null; do sleep 3; done'
23+
kubectl wait --for=condition=Ready node --all --timeout=90s
2424
kubectl create namespace integr8scode --dry-run=client -o yaml | kubectl apply -f -
2525
sed -E 's#https://(127\.0\.0\.1|0\.0\.0\.0):6443#https://host.docker.internal:6443#g' \
2626
/home/runner/.kube/config > backend/kubeconfig.yaml
@@ -32,9 +32,11 @@ runs:
3232
cp backend/config.test.toml backend/config.toml
3333
cp backend/secrets.example.toml backend/secrets.toml
3434
35-
- name: Pre-pull test runtime image into K3s
35+
- name: Pre-pull test runtime images into K3s
3636
shell: bash
37-
run: sudo k3s crictl pull docker.io/library/python:3.11-slim
37+
run: |
38+
sudo k3s crictl pull docker.io/library/python:3.11-slim
39+
sudo k3s crictl pull docker.io/library/busybox:1.36
3840
3941
- name: Wait for image pull and infra
4042
shell: bash

.github/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
run: uv tool install mkdocs --with mkdocs-material --with mkdocs-mermaid2-plugin --with mkdocs-swagger-ui-tag
4141

4242
- name: Install backend dependencies
43-
run: cd backend && uv sync --frozen
43+
run: cd backend && uv sync --frozen --no-dev
4444

4545
- name: Set up config for OpenAPI generation
4646
run: cp backend/secrets.example.toml backend/secrets.toml

.github/workflows/mypy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
run: |
2525
cd backend
2626
uv python install 3.12
27-
uv sync --frozen
27+
uv sync --frozen --group lint --no-dev
2828
2929
- name: Run mypy
3030
env:

.github/workflows/ruff.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
run: |
2525
cd backend
2626
uv python install 3.12
27-
uv sync --frozen
27+
uv sync --frozen --group lint --no-dev
2828
2929
- name: Run ruff
3030
run: |

.github/workflows/stack-tests.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
run: |
5151
cd backend
5252
uv python install 3.12
53-
uv sync --frozen
53+
uv sync --frozen --group test --no-dev
5454
5555
- name: Set up config
5656
run: cp backend/secrets.example.toml backend/secrets.toml
@@ -170,7 +170,9 @@ jobs:
170170
# ── Backend (depends on local base image) ───────────────
171171
- name: Build backend image
172172
run: |
173-
docker build -t integr8scode-backend:latest --build-context base=docker-image://integr8scode-base:latest -f ./backend/Dockerfile ./backend
173+
docker build -t integr8scode-backend:latest \
174+
--build-context base=docker-image://integr8scode-base:latest \
175+
-f ./backend/Dockerfile ./backend
174176
175177
# ── Utility images (GHA-cached, independent of base) ────────────
176178
- name: Build cert-generator image
@@ -248,11 +250,11 @@ jobs:
248250
timeout-minutes: 15
249251
run: |
250252
docker compose exec -T backend \
251-
uv run pytest tests/e2e -v -rs \
253+
sh -c 'uv sync --group test --no-dev --frozen --no-install-project && pytest tests/e2e -v -rs \
252254
--durations=0 \
253255
--cov=app \
254256
--cov-report=xml:coverage-e2e.xml \
255-
--cov-report=term
257+
--cov-report=term'
256258
257259
- name: Copy coverage
258260
if: always()

.github/workflows/vulture.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
run: |
2525
cd backend
2626
uv python install 3.12
27-
uv sync --frozen
27+
uv sync --frozen --group lint --no-dev
2828
2929
- name: Run vulture
3030
run: |

backend/.dockerignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,18 @@ htmlcov/
2323
# Local dev files
2424
*.log
2525
.DS_Store
26+
27+
# Tests and docs (not needed in production image)
28+
tests/
29+
docs/
30+
*.md
31+
32+
# Secrets and test/override configs (not needed in image)
33+
secrets.toml
34+
secrets.*.toml
35+
config.test.toml
36+
config.*.toml
37+
!config.toml
38+
39+
# Dead code detection whitelist
40+
vulture_whitelist.py

backend/Dockerfile.base

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,52 @@
11
# Shared base image for all backend services
22
# Contains: Python, system deps, uv, and all Python dependencies
3-
FROM python:3.12-slim
3+
# Multi-stage build: gcc + dev headers only in builder, not in final image
4+
5+
FROM python:3.12-slim AS builder
46

57
WORKDIR /app
68

7-
# Install OS security patches + system dependencies needed by any service
8-
RUN apt-get update && apt-get upgrade -y \
9+
# Install build-time dependencies (gcc + dev headers for C extensions)
10+
RUN apt-get update \
911
&& apt-get install -y --no-install-recommends \
1012
gcc \
11-
curl \
1213
libsnappy-dev \
1314
liblzma-dev \
1415
&& rm -rf /var/lib/apt/lists/*
1516

1617
# Install uv (using Docker Hub mirror - ghcr.io has rate limiting issues)
1718
COPY --from=astral/uv:latest /uv /uvx /bin/
1819

20+
# Pre-compile bytecode for faster startup; copy mode avoids symlink issues with cache mounts
21+
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
22+
1923
# Copy dependency files
2024
COPY pyproject.toml uv.lock ./
2125

22-
# Install Python dependencies (production only)
23-
RUN uv sync --locked --no-dev --no-install-project
26+
# Install Python dependencies with BuildKit cache mount for faster rebuilds
27+
RUN --mount=type=cache,target=/root/.cache/uv \
28+
uv sync --locked --no-dev --no-install-project
29+
30+
FROM python:3.12-slim
31+
32+
WORKDIR /app
33+
34+
# Install only runtime dependencies (shared libs, no -dev headers, no gcc)
35+
RUN apt-get update && apt-get upgrade -y \
36+
&& apt-get install -y --no-install-recommends \
37+
curl \
38+
libsnappy1v5 \
39+
liblzma5 \
40+
&& rm -rf /var/lib/apt/lists/*
41+
42+
# Copy uv from builder (avoids second Docker Hub pull, guarantees version consistency)
43+
COPY --from=builder /bin/uv /bin/uvx /bin/
44+
45+
# Copy pre-built virtual environment from builder stage
46+
COPY --from=builder /app/.venv /app/.venv
47+
48+
# Copy dependency files (needed for uv to recognize the project)
49+
COPY pyproject.toml uv.lock ./
2450

2551
# Set paths: PYTHONPATH for imports, PATH for venv binaries (no uv run needed at runtime)
2652
ENV PYTHONPATH=/app

backend/Dockerfile.test

Lines changed: 0 additions & 28 deletions
This file was deleted.

backend/app/api/routes/admin/events.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from dishka.integrations.fastapi import DishkaRoute
77
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query
88
from fastapi.responses import StreamingResponse
9+
from sse_starlette.sse import EventSourceResponse
910

1011
from app.api.dependencies import admin_user
1112
from app.domain.enums import EventType, ExportFormat
@@ -24,6 +25,16 @@
2425
)
2526
from app.schemas_pydantic.common import ErrorResponse
2627
from app.services.admin import AdminEventsService
28+
from app.services.sse import SSEService
29+
30+
31+
class _SSEResponse(EventSourceResponse):
32+
"""Workaround: sse-starlette sets media_type only in __init__, not as a
33+
class attribute. FastAPI reads the class attribute for OpenAPI generation,
34+
so without this subclass every SSE endpoint shows application/json."""
35+
36+
media_type = "text/event-stream"
37+
2738

2839
router = APIRouter(
2940
prefix="/admin/events", tags=["admin-events"], route_class=DishkaRoute, dependencies=[Depends(admin_user)]
@@ -133,16 +144,25 @@ async def replay_events(
133144

134145
@router.get(
135146
"/replay/{session_id}/status",
136-
responses={404: {"model": ErrorResponse, "description": "Replay session not found"}},
147+
response_class=_SSEResponse,
148+
responses={
149+
200: {"model": EventReplayStatusResponse},
150+
404: {"model": ErrorResponse, "description": "Replay session not found"},
151+
},
137152
)
138-
async def get_replay_status(session_id: str, service: FromDishka[AdminEventsService]) -> EventReplayStatusResponse:
139-
"""Get the status and progress of a replay session."""
140-
status = await service.get_replay_status(session_id)
153+
async def stream_replay_status(
154+
session_id: str,
155+
service: FromDishka[AdminEventsService],
156+
sse_service: FromDishka[SSEService],
157+
) -> EventSourceResponse:
158+
"""Stream the status and progress of a replay session via SSE."""
159+
status = await service.get_replay_sse_status(session_id)
141160

142161
if not status:
143162
raise HTTPException(status_code=404, detail="Replay session not found")
144163

145-
return EventReplayStatusResponse.model_validate(status)
164+
stream = await sse_service.create_replay_stream(status)
165+
return EventSourceResponse(stream, ping=15)
146166

147167

148168
@router.delete("/{event_id}", responses={404: {"model": ErrorResponse, "description": "Event not found"}})

0 commit comments

Comments
 (0)