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
42 changes: 42 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Git
.git
.gitignore
.gitattributes

# Python
__pycache__
*.py[cod]
*$py.class
*.so
.Python
*.egg-info
dist
build
*.egg
.venv
.venv-*
venv
env

# Testing
.pytest_cache
.coverage
htmlcov
.mypy_cache
.ruff_cache

# IDE
.vscode
.idea
*.swp
*.swo
*~

# CI/CD
.github

# Other
.DS_Store
*.log
tmp
temp
77 changes: 28 additions & 49 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,17 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
run: uv python install 3.11

- name: Install dependencies
run: |
python -m pip install --upgrade pip
cd packages/gptzero
pip install -e ".[dev]"
run: uv sync --all-packages --group dev

- name: Run tests
run: |
cd packages/gptzero
pytest tests/ -v --cov=gptzero --cov-report=term-missing --cov-report=xml
run: uv run --package gptzero pytest packages/gptzero/tests/ -v --cov=gptzero --cov-report=term-missing --cov-report=xml

- name: Upload coverage
uses: codecov/codecov-action@v4
Expand All @@ -52,23 +48,17 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
run: uv python install 3.11

- name: Install dependencies
run: |
python -m pip install --upgrade pip
cd packages/gptzero
pip install -e .
cd ../gptzero-api
pip install -e ".[dev]"
run: uv sync --all-packages --group dev

- name: Run linting
run: |
cd packages/gptzero-api
ruff check src/
run: uv run --package gptzero-api ruff check packages/gptzero-api/src/

test-sdk:
name: Test GPTZero SDK Client
Expand All @@ -79,21 +69,17 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
run: uv python install 3.11

- name: Install dependencies
run: |
python -m pip install --upgrade pip
cd packages/gptzero-sdk
pip install -e ".[dev]"
run: uv sync --all-packages --group dev

- name: Run linting
run: |
cd packages/gptzero-sdk
ruff check src/
run: uv run --package gptzero-sdk ruff check packages/gptzero-sdk/src/

lint:
name: Lint All Packages
Expand All @@ -104,30 +90,23 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
run: uv python install 3.11

- name: Install ruff
run: pip install ruff
- name: Install dependencies
run: uv sync --all-packages --group dev

- name: Lint gptzero
run: |
cd packages/gptzero
ruff check src/ tests/
run: uv run ruff check packages/gptzero/src/ packages/gptzero/tests/

- name: Lint gptzero-api
run: |
cd packages/gptzero-api
ruff check src/
run: uv run ruff check packages/gptzero-api/src/

- name: Lint gptzero-sdk
run: |
cd packages/gptzero-sdk
ruff check src/
run: uv run ruff check packages/gptzero-sdk/src/

- name: Lint gptzero-service
run: |
cd packages/gptzero-service
ruff check src/
run: uv run ruff check packages/gptzero-service/src/
105 changes: 70 additions & 35 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,49 +1,80 @@
# Multi-stage Dockerfile for running both API and Service
# Multi-stage Dockerfile for running both API and Service with optimized layer caching

FROM ubuntu:24.04 AS base
# Use specific Python version for consistency
ARG PYTHON_VERSION=3.12
# Use Ubuntu 24.04 for GLIBC 2.39 compatibility (required by c2patool)
ARG PYTHON_IMAGE=ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive
# Stage 1: Builder - Install dependencies with uv
FROM ghcr.io/astral-sh/uv:python${PYTHON_VERSION}-bookworm-slim AS builder

RUN apt update && apt install -y \
python3 \
python3-pip \
python3-venv \
python3-dev \
build-essential \
curl \
software-properties-common \
&& rm -rf /var/lib/apt/lists/*

RUN ln -s /usr/bin/python3 /usr/bin/python
# Enable bytecode compilation for faster startup
ENV UV_COMPILE_BYTECODE=1
# Use copy mode to ensure dependencies are copied to final image
ENV UV_LINK_MODE=copy
# Use system Python interpreter (no managed Python download)
ENV UV_PYTHON_DOWNLOADS=0
# Omit development dependencies
ENV UV_NO_DEV=1

WORKDIR /app

# Create virtual environment
RUN python -m venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
# Copy workspace configuration and package definitions
# This allows uv to understand workspace structure without copying source code
COPY pyproject.toml uv.lock ./
COPY packages/gptzero/pyproject.toml packages/gptzero/README.md ./packages/gptzero/
COPY packages/gptzero-sdk/pyproject.toml packages/gptzero-sdk/README.md ./packages/gptzero-sdk/
COPY packages/gptzero-api/pyproject.toml packages/gptzero-api/README.md ./packages/gptzero-api/
COPY packages/gptzero-service/pyproject.toml packages/gptzero-service/README.md ./packages/gptzero-service/

# Upgrade pip
RUN python -m pip install --upgrade pip
# Install dependencies (this layer is cached unless lock/config files change)
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --no-install-project --all-packages

# Copy all packages
COPY packages/ /app/packages/
# Then, copy the rest of the source code
COPY . /app

# Install gptzero (core SDK)
RUN cd /app/packages/gptzero && pip install --no-cache-dir -e .
# Install the workspace packages (source code)
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --all-packages

# Make c2patool executable
RUN chmod +x /app/packages/gptzero/resources/c2patool/v0.16.1/Linux/c2patool || true

# Install gptzero-api
RUN cd /app/packages/gptzero-api && pip install --no-cache-dir -e .
# Stage 2: Final runtime image without uv
FROM ${PYTHON_IMAGE}

ARG PYTHON_VERSION

# Create non-root user for security
RUN groupadd --system --gid 999 appuser \
&& useradd --system --gid 999 --uid 999 --create-home appuser

# Install Python and curl for healthchecks
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
python${PYTHON_VERSION} \
python${PYTHON_VERSION}-venv \
curl \
&& ln -sf /usr/bin/python${PYTHON_VERSION} /usr/bin/python3 \
&& ln -sf /usr/bin/python${PYTHON_VERSION} /usr/bin/python \
&& mkdir -p /usr/local/bin \
&& ln -sf /usr/bin/python${PYTHON_VERSION} /usr/local/bin/python3 \
&& ln -sf /usr/bin/python${PYTHON_VERSION} /usr/local/bin/python \
&& rm -rf /var/lib/apt/lists/*

# Install gptzero-sdk
RUN cd /app/packages/gptzero-sdk && pip install --no-cache-dir -e .
# Copy the application and virtual environment from builder
COPY --from=builder --chown=appuser:appuser /app /app

# Install gptzero-service
RUN cd /app/packages/gptzero-service && pip install --no-cache-dir -e .
# Set up environment to use the virtual environment
ENV PATH="/app/.venv/bin:$PATH"
# Ensure Python uses system packages from venv
ENV VIRTUAL_ENV="/app/.venv"

# Make c2patool executable
RUN chmod +x /app/packages/gptzero/resources/c2patool/v0.16.1/Linux/c2patool || true
# Set working directory
WORKDIR /app

# Expose ports
# Expose ports for API and Service
EXPOSE 8000 8501

# Health check for both services
Expand All @@ -54,7 +85,7 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
RUN echo '#!/bin/bash\n\
set -e\n\
echo "Starting GPTZero API on port 8000..."\n\
uvicorn gptzero_api.api:app --host 0.0.0.0 --port 8000 &\n\
/app/.venv/bin/gptzero-api &\n\
API_PID=$!\n\
echo "API started with PID $API_PID"\n\
\n\
Expand All @@ -63,12 +94,16 @@ sleep 5\n\
\n\
echo "Starting GPTZero Service on port 8501..."\n\
export GPTZERO_API_URL=http://localhost:8000\n\
streamlit run /app/packages/gptzero-service/src/handler.py --server.port=8501 --server.address=0.0.0.0 &\n\
/app/.venv/bin/streamlit run /app/packages/gptzero-service/src/handler.py --server.port=8501 --server.address=0.0.0.0 &\n\
SERVICE_PID=$!\n\
echo "Service started with PID $SERVICE_PID"\n\
\n\
# Wait for both processes\n\
wait $API_PID $SERVICE_PID\n\
' > /app/start.sh && chmod +x /app/start.sh
' > /app/start.sh && chmod +x /app/start.sh \
&& chown appuser:appuser /app/start.sh

# Switch to non-root user
USER appuser

CMD ["/app/start.sh"]
Loading