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
7 changes: 0 additions & 7 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,6 @@ Dockerfile
.dockerignore
docker-compose.yml

# Grafana & Prometheus (not needed in app container)
grafana/
prometheus/

# Postman collections
postman/

# Tests
tests/

Expand Down
6 changes: 3 additions & 3 deletions .github/actions/setup-python-core/action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Setup Python Environment (Core)
description: Sets up Python with uv and installs core dependencies
description: Sets up Python with uv and creates a virtual environment

runs:
using: composite
Expand All @@ -13,7 +13,7 @@ runs:
uses: actions/setup-python@v5
with:
python-version-file: ".python-version"
- name: Install dependencies
- name: Create virtual environment
run: |
uv sync
uv venv
shell: bash
103 changes: 103 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Build

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build_wheel:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-python-dev
- name: Create wheel
run: |
uv build
- name: Inspect wheel contents
run: |
echo "Wheel contents:"
unzip -l dist/python_template_server-*-py3-none-any.whl
- name: Upload wheel
uses: actions/upload-artifact@v4
with:
name: python_template_server_wheel
path: dist/python_template_server-*-py3-none-any.whl

verify_structure:
runs-on: ubuntu-latest
needs: build_wheel
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-python-core

- name: Download wheel artifact
uses: actions/download-artifact@v4
with:
name: python_template_server_wheel

- name: Install wheel
run: |
PACKAGE_NAME="python_template_server"
WHEEL_FILE=$(find . -name "${PACKAGE_NAME}-*-py3-none-any.whl")

if [ ! -f "$WHEEL_FILE" ]; then
echo "Wheel file not found"
exit 1
fi

echo "Installing wheel: $WHEEL_FILE"
uv pip install "$WHEEL_FILE"

- name: Verify installed package structure
run: |
PACKAGE_NAME="python_template_server"

# Find site-packages directory
SITE_PACKAGES=$(find . -name "site-packages" -type d | head -1)
echo "Site-packages directory: $SITE_PACKAGES"

# Check for required directories in site-packages
echo "Checking required directories in site-packages..."
REQUIRED_DIRS=(
"${SITE_PACKAGES}/${PACKAGE_NAME}"
"${SITE_PACKAGES}/configuration"
"${SITE_PACKAGES}/grafana"
"${SITE_PACKAGES}/prometheus"
)

for dir in "${REQUIRED_DIRS[@]}"; do
if [ ! -d "$dir" ]; then
echo "Required directory not found: $dir"
exit 1
fi
done

# Check for required files in site-packages
echo "Checking required files in site-packages..."
REQUIRED_FILES=(
"${SITE_PACKAGES}/configuration/config.json"
"${SITE_PACKAGES}/README.md"
"${SITE_PACKAGES}/LICENSE"
"${SITE_PACKAGES}/.here"
)

for file in "${REQUIRED_FILES[@]}"; do
if [ ! -f "$file" ]; then
echo "Required file not found: $file"
exit 1
fi
done

# Show directory structure
echo "Package structure in site-packages:"
tree "${SITE_PACKAGES}/${PACKAGE_NAME}" --dirsfirst -F -L 2
echo "Configuration structure:"
tree "${SITE_PACKAGES}/configuration" --dirsfirst -F
echo "Grafana structure:"
tree "${SITE_PACKAGES}/grafana" --dirsfirst -F -L 2
echo "Prometheus structure:"
tree "${SITE_PACKAGES}/prometheus" --dirsfirst -F
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,18 @@ jobs:
- name: Check with mypy
run: |
uv run -m mypy .
test:
pytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-python-dev
- name: Test with pytest
run: |
uv run -m pytest --cov-report html --cov-report term
- name: Upload coverage report
- name: Upload backend coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report
name: backend-coverage-report
path: htmlcov
bandit:
runs-on: ubuntu-latest
Expand Down
47 changes: 3 additions & 44 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,14 @@ on:
pull_request:
branches:
- main
workflow_dispatch:

jobs:
docker-development:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-python-core

- name: Create directories with proper permissions
run: |
mkdir -p certs logs
chmod 777 certs logs
sudo chown -R 1000:1000 certs logs

- name: Build and start services with docker compose
run: |
Expand All @@ -35,39 +29,4 @@ jobs:
port: 443

- name: Stop services
run: docker compose down

docker-production:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-python-core

- name: Create directories with proper permissions
run: |
mkdir -p certs logs
chmod 777 certs logs
sudo chown -R 1000:1000 certs logs

- name: Build production image
run: |
docker build --build-arg ENV=prod --build-arg PORT=443 -t python-template-server:prod .

- name: Start services with docker compose
env:
ENV: prod
PORT: 443
run: |
docker compose up -d
sleep 5 # Wait for container to start

- name: Show server logs
run: docker logs python-template-server

- uses: ./.github/actions/docker-check-containers
with:
port: 443

- name: Stop services
run: docker compose down
run: docker compose --profile cpu down --volumes --remove-orphans
62 changes: 34 additions & 28 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,77 +1,83 @@
# Multi-stage Dockerfile for Python Template Server
# Stage 1: Build stage - build wheel using uv
FROM python:3.13-slim AS builder
FROM python:3.13-slim AS backend-builder

WORKDIR /build

# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

# Copy project files
# Copy backend source files
COPY python_template_server/ ./python_template_server/
COPY configuration ./configuration/
COPY configuration/ ./configuration/
COPY grafana/ ./grafana/
COPY prometheus/ ./prometheus/
COPY pyproject.toml .here LICENSE README.md ./

# Build the wheel
RUN uv build --wheel

# Stage 2: Runtime stage
# Stage 3: Runtime stage
FROM python:3.13-slim

# Build arguments for environment-specific config
ARG ENV=dev
ARG PORT=443

WORKDIR /app

# Create non-root user for security
RUN useradd -m -u 1000 template_server_user && \
chown -R template_server_user:template_server_user /app

# Install uv in runtime stage
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

# Copy the built wheel from builder
COPY --from=builder /build/dist/*.whl /tmp/
# Copy the built wheel from backend builder
COPY --from=backend-builder /build/dist/*.whl /tmp/

# Install the wheel
RUN uv pip install --system --no-cache /tmp/*.whl && \
rm /tmp/*.whl

# Create configuration directory
RUN mkdir -p /app/configuration && \
chown template_server_user:template_server_user /app/configuration
# Create required directories
RUN mkdir -p /app/logs /app/certs

# Copy included files from installed wheel
# Copy included files from installed wheel to app directory
RUN SITE_PACKAGES_DIR=$(find /usr/local/lib -name "site-packages" -type d | head -1) && \
cp -r "${SITE_PACKAGES_DIR}/configuration" /app/ && \
cp -r "${SITE_PACKAGES_DIR}/grafana" /app/ && \
cp -r "${SITE_PACKAGES_DIR}/prometheus" /app/ && \
cp "${SITE_PACKAGES_DIR}/.here" /app/.here && \
cp "${SITE_PACKAGES_DIR}/configuration/config.json" /app/configuration/config.json && \
cp "${SITE_PACKAGES_DIR}/LICENSE" /app/LICENSE && \
cp "${SITE_PACKAGES_DIR}/README.md" /app/README.md

# Create startup script
# Create startup script with Ollama model checking
RUN echo '#!/bin/sh\n\
if [ ! -f .env ]; then\n\
set -e\n\
\n\
# Copy monitoring configs to shared volume if they do not exist\n\
if [ -d "/monitoring-configs" ]; then\n\
echo "Setting up monitoring configurations..."\n\
mkdir -p /monitoring-configs/prometheus /monitoring-configs/grafana\n\
cp -r /app/prometheus/* /monitoring-configs/prometheus/ 2>/dev/null || true\n\
cp -r /app/grafana/* /monitoring-configs/grafana/ 2>/dev/null || true\n\
echo "Monitoring configurations ready"\n\
fi\n\
\n\
# Generate API token if needed\n\
if [ -z "$API_TOKEN_HASH" ]; then\n\
echo "Generating new token..."\n\
generate-new-token\n\
export $(grep -v "^#" .env | xargs)\n\
fi\n\
\n\
# Generate certificates if needed\n\
if [ ! -f certs/cert.pem ] || [ ! -f certs/key.pem ]; then\n\
echo "Generating self-signed certificates..."\n\
generate-certificate\n\
fi\n\
\n\
exec python-template-server' > /app/start.sh && \
chmod +x /app/start.sh && \
chown template_server_user:template_server_user /app/start.sh

# Switch to non-root user
USER template_server_user
chmod +x /app/start.sh

# Expose HTTPS port
EXPOSE $PORT
EXPOSE 443

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('https://localhost:$PORT/api/health', context=__import__('ssl')._create_unverified_context()).read()" || exit 1
CMD python -c "import urllib.request; urllib.request.urlopen('https://localhost:443/api/health', context=__import__('ssl')._create_unverified_context()).read()" || exit 1

CMD ["/app/start.sh"]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![FastAPI](https://img.shields.io/badge/FastAPI-Latest-009688?style=flat&logo=fastapi&logoColor=white)](https://fastapi.tiangolo.com/)
[![CI](https://img.shields.io/github/actions/workflow/status/javidahmed64592/python-template-server/ci.yml?branch=main&style=flat-square&label=CI&logo=github)](https://github.com/javidahmed64592/python-template-server/actions/workflows/ci.yml)
[![Build](https://img.shields.io/github/actions/workflow/status/javidahmed64592/python-template-server/build.yml?branch=main&style=flat-square&label=Build&logo=github)](https://github.com/javidahmed64592/python-template-server/actions/workflows/build.yml)
[![Docker](https://img.shields.io/github/actions/workflow/status/javidahmed64592/python-template-server/docker.yml?branch=main&style=flat-square&label=Docker&logo=github)](https://github.com/javidahmed64592/python-template-server/actions/workflows/docker.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Expand Down
Loading