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
46 changes: 31 additions & 15 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,64 @@ FROM python:3.13-slim-bookworm
# Install UV (ultra-fast Python package installer) from Astral.sh
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

# Create non-root user for security
RUN groupadd -r appuser && useradd -r -g appuser appuser

# Ensure Python output is sent straight to terminal without buffering
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
PYTHONDONTWRITEBYTECODE=1 \
UV_SYSTEM_PYTHON=1

# ======================
# SYSTEM DEPENDENCIES
# ======================
RUN apt-get update && \
apt-get install -y --no-install-recommends curl gettext && \
apt-get clean && rm -rf /var/lib/apt/lists/*
apt-get install -y --no-install-recommends \
curl \
gettext \
ca-certificates && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /tmp/* /var/tmp/*

# Set working directory inside container
WORKDIR /app

# ======================
# DEPENDENCY INSTALLATION
# ======================
# Copies the dependency files (py project.tool and uv.lock) to /app.
COPY pyproject.toml uv.lock ./
# Copy dependency files with proper ownership
COPY --chown=appuser:appuser pyproject.toml uv.lock ./

# Install Python dependencies using UV:
# --locked: ensures exact versions from lockfile are used
# Copies the rest of the project code to /app.
RUN uv sync --locked
# Install Python dependencies using UV
RUN uv sync --locked --no-dev

# ======================
# APPLICATION CODE
# ======================
# Copy entrypoint script first
COPY --chown=appuser:appuser entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh

# Copy the rest of the application code
# Note: This is done after dependency installation for better caching
COPY . .
COPY --chown=appuser:appuser . .

# Copies the script entrypoint.sh in /app.
# Makes it executable (chmod +x).
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
# Create directories for Django with proper permissions
RUN mkdir -p /app/static /app/media && \
chown -R appuser:appuser /app

# ======================
# RUNTIME CONFIGURATION
# ======================
# Switch to non-root user
USER appuser

# Expose the port Django runs on
EXPOSE 8000

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health/ || exit 1

# Launches the application through a script entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]
120 changes: 109 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,31 +1,129 @@
# ======================
# VARIABLES
# ======================
USERID := $(shell id -u)
GROUPID := $(shell id -g)
PYTHON := docker compose run -u $(USERID):$(GROUPID) --rm django uv run
DOCKER_COMPOSE := docker compose
RUFF := uvx ruff

collectstatic:
# ======================
# HELP
# ======================
.PHONY: help
help: ## Show this help message
@echo "Available commands:"
@awk 'BEGIN {FS = ":.*##"; printf "\n"} /^[a-zA-Z_-]+:.*?##/ { printf " %-20s %s\n", $$1, $$2 } /^##@/ { printf "\n%s\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

# ======================
# DOCKER MANAGEMENT
# ======================
.PHONY: build up down restart logs clean
build: ## Build Docker images
$(DOCKER_COMPOSE) build

up: ## Start all services
$(DOCKER_COMPOSE) up -d

down: ## Stop all services
$(DOCKER_COMPOSE) down

restart: ## Restart all services
$(DOCKER_COMPOSE) restart

logs: ## Show logs for all services
$(DOCKER_COMPOSE) logs -f

clean: ## Clean up Docker resources
$(DOCKER_COMPOSE) down -v --remove-orphans
docker system prune -f

# ======================
# DJANGO MANAGEMENT
# ======================
.PHONY: collectstatic startapp makemigrations migrate createsuperuser shell reset_db
collectstatic: ## Collect static files
$(PYTHON) manage.py collectstatic --noinput -c

startapp:
startapp: ## Create new Django app (usage: make startapp app=myapp)
$(PYTHON) manage.py startapp ${app}

makemigrations:
makemigrations: ## Create database migrations
$(PYTHON) manage.py makemigrations ${app}

migrate:
migrate: ## Apply database migrations
$(PYTHON) manage.py migrate ${app}

createsuperuser:
createsuperuser: ## Create Django superuser
$(PYTHON) manage.py createsuperuser

shell:
shell: ## Open Django shell
$(PYTHON) manage.py shell_plus

reset_db:
reset_db: ## Reset database
$(PYTHON) manage.py reset_db

format:
uvx ruff check --fix apps config && uvx ruff check --select I --fix apps config && uvx ruff format apps config
# ======================
# CODE QUALITY
# ======================
.PHONY: format lint check test
format: ## Format code with ruff
$(RUFF) check --fix apps config
$(RUFF) check --select I --fix apps config
$(RUFF) format apps config

lint: ## Run linting checks
$(RUFF) check apps config

check: ## Run all code quality checks
$(RUFF) check apps config
$(RUFF) format --check apps config

test: ## Run tests
$(PYTHON) manage.py test

run:
$(PYTHON) manage.py migrate
# ======================
# DEVELOPMENT
# ======================
.PHONY: run dev install requirements
run: migrate ## Run Django development server
$(PYTHON) manage.py runserver 0.0.0.0:8000

dev: up ## Start development environment
@echo "Development environment started"
@echo "Django: http://localhost:8000"
@echo "PostgreSQL: localhost:5432"

install: ## Install dependencies
uv sync --locked

requirements: ## Update requirements
uv lock

# ======================
# PRODUCTION
# ======================
.PHONY: prod-build prod-up prod-down
prod-build: ## Build production images
$(DOCKER_COMPOSE) -f docker-compose.prod.yml build

prod-up: ## Start production environment
$(DOCKER_COMPOSE) -f docker-compose.prod.yml up -d

prod-down: ## Stop production environment
$(DOCKER_COMPOSE) -f docker-compose.prod.yml down

# ======================
# UTILITIES
# ======================
.PHONY: backup restore health
backup: ## Create database backup
$(DOCKER_COMPOSE) exec postgres pg_dump -U $$POSTGRES_USER $$POSTGRES_DB > backup_$$(date +%Y%m%d_%H%M%S).sql

restore: ## Restore database from backup (usage: make restore file=backup.sql)
$(DOCKER_COMPOSE) exec -T postgres psql -U $$POSTGRES_USER $$POSTGRES_DB < ${file}

health: ## Check services health
$(DOCKER_COMPOSE) ps
@echo "\n--- Service Health ---"
@curl -f http://localhost:8000/health/ || echo "Django service not responding"
Loading