Skip to content
Open
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
18 changes: 8 additions & 10 deletions .github/workflows/tests-backend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,12 @@ jobs:
python-version: ["3.14"]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
- uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install pre-commit
enable-cache: true
- name: Run pre-commit
run: pre-commit run --all --verbose
run: uvx pre-commit run --all-files --verbose

test-backend:
runs-on: ubuntu-latest
Expand All @@ -43,17 +42,16 @@ jobs:
python-version: ["3.14"]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
- uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install backend dependencies with Python ${{ matrix.python-version }}
enable-cache: true
- name: Install backend dependencies
working-directory: ./backend
run: |
python -m pip install --upgrade pip wheel
pip install hatch
run: uv sync --frozen
- name: Run backend tests with coverage
working-directory: ./backend
run: hatch run test-cov
run: uv run pytest --cov=ibutsu_server --cov-report=xml --cov-report=term
- name: Upload coverage reports
uses: actions/upload-artifact@v7
with:
Expand Down
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,8 @@ podman build -t ibutsu-flower:<branch-name> -f docker/Dockerfile.flower .

## Database Migrations
- **Always use Alembic** for database schema changes
- Run `hatch run alembic revision -m "description"` to create new migrations
- Test migrations with `hatch run alembic upgrade head` and `hatch run alembic downgrade -1`
- Run `uv run alembic revision -m "description"` to create new migrations
- Test migrations with `uv run alembic upgrade head` and `uv run alembic downgrade -1`
- Never modify the database schema directly in production
- All schema changes must have both upgrade() and downgrade() functions
- Document any PostgreSQL-specific features in migration comments
Expand Down Expand Up @@ -315,8 +315,8 @@ podman build -t ibutsu-flower:<branch-name> -f docker/Dockerfile.flower .

# Backend Testing Instructions
- Find the CI plan in the .github/workflows folder.
- Use `hatch run test` to execute tests and `hatch run test-cov` to include coverage from the backend directory as the working directory
- Pass arguments to pytest through `hatch run test -- <-arg>`
- Use `uv run pytest` to execute tests and `uv run pytest --cov=ibutsu_server --cov-report=xml --cov-report=term --cov-report=html` to include coverage from the backend directory as the working directory
- Pass arguments to pytest through `uv run pytest <-arg>`
- Add or update tests for the code you change, even if nobody asked.
- Do not make changes to cause tests to pass when a bug is identified by the test. Always first investigate for bugs in the tested application component.
- Use full UUID strings for all `id` fields including `run_id` and `result_id` when mocking unless an invalid UUID is specifically being tested
Expand All @@ -330,7 +330,7 @@ podman build -t ibutsu-flower:<branch-name> -f docker/Dockerfile.flower .

## Coverage Requirements
- **Target**: 80% line coverage for all modules
- **Run**: `hatch run test-cov` to verify coverage
- **Run**: `uv run pytest --cov=ibutsu_server --cov-report=xml --cov-report=term --cov-report=html` to verify coverage
- Coverage reports generated in `htmlcov/` and `coverage.xml`

## Available Test Fixtures
Expand Down
1 change: 1 addition & 0 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ coverage.xml
*,cover
.hypothesis/
venv/
.venv/
.python-version

# Test directories and files
Expand Down
35 changes: 27 additions & 8 deletions backend/docker/Dockerfile.backend
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
# hadolint global ignore=DL3013,DL3041,DL3059
FROM registry.access.redhat.com/ubi9/python-314-minimal:9.8-1788215168
# hadolint global ignore=DL3041,DL3059
FROM registry.access.redhat.com/ubi9/python-314:1-1787618080 AS builder

ARG UV_VERSION=0.12.10

ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy PATH="/app/.venv/bin:$PATH"

USER 0

WORKDIR /app

RUN pip install --no-cache-dir "uv==${UV_VERSION}"

COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-default-groups --group server --no-install-project

COPY . /app
COPY licenses/ /licenses/
RUN /usr/bin/fix-permissions /app
RUN uv sync --frozen --no-dev --no-default-groups --group server

USER 1001

FROM registry.access.redhat.com/ubi9/python-314-minimal:9.8-1788215168

ENV PATH="/app/.venv/bin:$PATH"

USER 0

WORKDIR /app

RUN microdnf install --nodocs -y file-libs && \
microdnf clean all

RUN python -m pip install --no-cache-dir -U pip wheel setuptools && \
pip install --no-cache-dir -U -r requirements-pinned.txt .

RUN chgrp -R 0 ibutsu_server && chmod -R g+rwX ibutsu_server
COPY --from=builder /app /app
COPY licenses/ /licenses/
RUN /usr/bin/fix-permissions /app

USER 1001

Expand Down
40 changes: 29 additions & 11 deletions backend/docker/Dockerfile.flower
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
# hadolint global ignore=DL3013,DL3041,DL3025
# hadolint global ignore=DL3041,DL3025,DL3059
FROM registry.access.redhat.com/ubi9/python-314:1-1787618080 AS builder

ARG UV_VERSION=0.12.10

ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy PATH="/app/.venv/bin:$PATH"

USER 0

WORKDIR /app

RUN pip install --no-cache-dir "uv==${UV_VERSION}"

COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-default-groups --only-group flower --no-install-project

COPY . /app
RUN uv sync --frozen --no-dev --no-default-groups --only-group flower

USER 1001

FROM registry.access.redhat.com/ubi9/python-314-minimal:9.8-1788215168

# add application sources with correct perms for OCP
ENV PATH="/app/.venv/bin:$PATH"

USER 0

WORKDIR /app

RUN microdnf install --nodocs -y file-libs && \
microdnf clean all

COPY . /app
COPY --from=builder /app /app
COPY licenses/ /licenses/
RUN /usr/bin/fix-permissions /app

USER 1001

# Install dependencies
WORKDIR /app
RUN python -m pip install --no-cache-dir -U pip wheel setuptools && \
pip install --no-cache-dir -r requirements-pinned.txt . && \
pip install --no-cache-dir 'flower>=2.0.0'

# Expose Flower default port
EXPOSE 5555

# Use flower_app - minimal broker-only app (no database required)
# flower_app is exposed via __getattr__ in ibutsu_server/__init__.py
CMD ["celery", "--app=ibutsu_server:flower_app", "flower", "--port=5555", "--enable_events=True"]
# flower_app is exposed via __getattr__ in ibutsu_server/celery_utils.py
CMD ["celery", "--app=ibutsu_server.celery_utils:flower_app", "flower", "--port=5555", "--enable_events=True"]
35 changes: 26 additions & 9 deletions backend/docker/Dockerfile.scheduler
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
# hadolint global ignore=DL3013,DL3041
FROM registry.access.redhat.com/ubi9/python-314-minimal:9.8-1788215168
# hadolint global ignore=DL3041,DL3059
FROM registry.access.redhat.com/ubi9/python-314:1-1787618080 AS builder

ARG UV_VERSION=0.12.10

ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy PATH="/app/.venv/bin:$PATH"

USER 0

ENV UPGRADE_PIP_TO_LATEST=1
WORKDIR /app

RUN microdnf install --nodocs -y file-libs && \
microdnf clean all
RUN pip install --no-cache-dir "uv==${UV_VERSION}"

COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-default-groups --group server --no-install-project

COPY . /app
COPY licenses/ /licenses/
RUN uv sync --frozen --no-dev --no-default-groups --group server

USER 1001

FROM registry.access.redhat.com/ubi9/python-314-minimal:9.8-1788215168

ENV PATH="/app/.venv/bin:$PATH"

USER 0

WORKDIR /app
RUN python -m pip install --no-cache-dir -U pip wheel setuptools && \
pip install --no-cache-dir -r requirements-pinned.txt .

RUN chgrp -R 0 ibutsu_server && chmod -R g+rwX ibutsu_server
RUN microdnf install --nodocs -y file-libs && \
microdnf clean all

COPY --from=builder /app /app
COPY licenses/ /licenses/
RUN /usr/bin/fix-permissions /app

USER 1001

Expand Down
35 changes: 26 additions & 9 deletions backend/docker/Dockerfile.worker
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
# hadolint global ignore=DL3013,DL3041
FROM registry.access.redhat.com/ubi9/python-314-minimal:9.8-1788215168
# hadolint global ignore=DL3041,DL3059
FROM registry.access.redhat.com/ubi9/python-314:1-1787618080 AS builder

ARG UV_VERSION=0.12.10

ENV UPGRADE_PIP_TO_LATEST=1
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy PATH="/app/.venv/bin:$PATH"

USER 0

RUN microdnf install --nodocs -y file-libs && \
microdnf clean all
WORKDIR /app

RUN pip install --no-cache-dir "uv==${UV_VERSION}"

COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-default-groups --group server --group worker --no-install-project

COPY . /app
COPY licenses/ /licenses/
RUN uv sync --frozen --no-dev --no-default-groups --group server --group worker

USER 1001

FROM registry.access.redhat.com/ubi9/python-314-minimal:9.8-1788215168

ENV PATH="/app/.venv/bin:$PATH"

USER 0

WORKDIR /app
RUN python -m pip install --no-cache-dir -U pip wheel setuptools && \
pip install --no-cache-dir -r requirements-pinned.txt .

RUN chgrp -R 0 ibutsu_server && chmod -R g+rwX ibutsu_server
RUN microdnf install --nodocs -y file-libs && \
microdnf clean all

COPY --from=builder /app /app
COPY licenses/ /licenses/
RUN /usr/bin/fix-permissions /app

USER 1001

Expand Down
7 changes: 4 additions & 3 deletions backend/ibutsu_server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,15 @@
from pathlib import Path
from typing import Any

import connexion
import flask
from flask_mail import Mail
from sqlalchemy import create_engine
from sqlalchemy.engine.url import URL as SQLA_URL
from starlette.middleware.cors import CORSMiddleware
from yaml import full_load as yaml_load

from ibutsu_server.db.base import db
from ibutsu_server.db.models import User
from ibutsu_server.db.util import add_superadmin
from ibutsu_server.encoder import IbutsuJSONProvider
from ibutsu_server.util.jwt import decode_token

FRONTEND_PATH = Path("/app/frontend")
Expand Down Expand Up @@ -64,6 +61,10 @@ def check_envvar(config: flask.Config, *, envvar: str) -> str:

def get_app(**extra_config):
"""Create the WSGI application for ASGI wrapper"""
import connexion # noqa: PLC0415
from starlette.middleware.cors import CORSMiddleware # noqa: PLC0415

from ibutsu_server.encoder import IbutsuJSONProvider # noqa: PLC0415

connexion_app = connexion.FlaskApp(
__name__, specification_dir="./openapi/", jsonifier=IbutsuJSONProvider()
Expand Down
20 changes: 19 additions & 1 deletion backend/ibutsu_server/celery_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,17 @@

import logging
import os
from typing import TYPE_CHECKING

from celery import Celery, signals
from celery.schedules import crontab

from ibutsu_server.constants import SOCKET_CONNECT_TIMEOUT, SOCKET_TIMEOUT
from ibutsu_server.util.celery_task import IbutsuTask, set_flask_app

if TYPE_CHECKING:
flower_app: Celery


def create_broker_celery_app(name="ibutsu_server_flower"):
"""
Expand Down Expand Up @@ -263,4 +267,18 @@ def retry_task_on_exception(*_args, **kwargs):
task.retry(countdown=backoff)


__all__ = ["create_broker_celery_app", "create_flask_celery_app", "retry_task_on_exception"]
def __getattr__(name: str):
"""Lazy initialization of module-level app instances."""
if name == "flower_app":
app = create_broker_celery_app()
globals()[name] = app
return app
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")


__all__ = [
"create_broker_celery_app",
"create_flask_celery_app",
"flower_app",
"retry_task_on_exception",
]
7 changes: 5 additions & 2 deletions backend/ibutsu_server/tasks/importers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

from celery.utils.log import get_task_logger
from dateutil import parser
from lxml import objectify

from ibutsu_server.db import db
from ibutsu_server.db.models import Artifact, Import, ImportFile, Result, Run
Expand Down Expand Up @@ -238,8 +237,10 @@ def _add_artifacts(result, testcase, traceback):
_upsert_result_artifact(result.id, "system-err.log", system_err)


def _get_properties(xml_element: objectify.Element) -> dict:
def _get_properties(xml_element) -> dict:
"""Get the properties from an XML element"""
from lxml import objectify # noqa: F401, PLC0415

if not hasattr(xml_element, "properties"):
return {}
properties = {}
Expand Down Expand Up @@ -402,6 +403,8 @@ def run_junit_import(import_):
# through rolls back cleanly and marks the import as errored.
with _import_failure_handling(import_record):
# Parse the XML and create a run object(s)
from lxml import objectify # noqa: PLC0415

tree = objectify.fromstring(import_file.content)
existing_run_id = _extract_run_id(import_record)
import_record.data["run_id"] = []
Expand Down
Loading
Loading