From 32925396dcd97419eba76d0fd816fc6b83b7ff6b Mon Sep 17 00:00:00 2001 From: ReachableCEO Date: Wed, 4 Feb 2026 18:05:41 -0500 Subject: [PATCH] feat: add Python app package template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create Python application Dockerfile template - Create Python app CloudronManifest.json template - Create Python app start.sh template - Include Django and Flask support - PostgreSQL integration with wait logic - Django migrations and collectstatic - Admin user creation support - Environment variable configuration - Health check implementation Template features: - Python 3-slim base image - PostgreSQL addon support - Localstorage addon support - Database wait and connection logic - Django-specific support (migrations, collectstatic, admin creation) - Flask support (simple startup) - Configurable workers (NUM_WORKERS) - Port 5000 (configurable) - Health check endpoint Environment variables: - DJANGO_SETTINGS_MODULE: Django settings module - SECRET_KEY: Django secret key - ALLOWED_HOSTS: Allowed hosts for Django - DATABASE_URL: PostgreSQL connection URL (auto-generated) - ADMIN_USERNAME: Admin username (optional) - ADMIN_EMAIL: Admin email (optional) - ADMIN_PASSWORD: Admin password (optional) - NUM_WORKERS: Gunicorn workers (default: 4) 💘 Generated with Crush Assisted-by: GLM-4.7 via Crush --- .../python-app/CloudronManifest.json.template | 31 ++++++++++ .../python-app/Dockerfile.template | 36 +++++++++++ .../python-app/start.sh.template | 59 +++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 Package-Templates/python-app/CloudronManifest.json.template create mode 100644 Package-Templates/python-app/Dockerfile.template create mode 100644 Package-Templates/python-app/start.sh.template diff --git a/Package-Templates/python-app/CloudronManifest.json.template b/Package-Templates/python-app/CloudronManifest.json.template new file mode 100644 index 0000000..72bcb44 --- /dev/null +++ b/Package-Templates/python-app/CloudronManifest.json.template @@ -0,0 +1,31 @@ +{ + "version": 1, + "manifestVersion": 2, + "type": "app", + "id": "io.cloudron.example-python-app", + "title": "Example Python App", + "description": "Example Python web application. Demonstrates basic Flask application with PostgreSQL integration for Cloudron packaging.", + "author": "TSYS Cloudron Project", + "website": "https://cloudron.io", + "contactEmail": "cloudron@tsys.dev", + "tagline": "Example Python web application", + "version": "1.0.0", + "healthCheckPath": "/", + "httpPort": 5000, + "memoryLimit": 512, + "addons": { + "localstorage": true, + "postgresql": { + "version": "14" + } + }, + "tcpPorts": { + "HTTP_PORT": { + "description": "Flask HTTP port", + "defaultValue": 5000 + } + }, + "mediaLinks": [], + "changelog": "Initial example Cloudron package", + "icon": "file://logo.png" +} diff --git a/Package-Templates/python-app/Dockerfile.template b/Package-Templates/python-app/Dockerfile.template new file mode 100644 index 0000000..05b4bd4 --- /dev/null +++ b/Package-Templates/python-app/Dockerfile.template @@ -0,0 +1,36 @@ +FROM python:3-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + libpq-dev \ + gcc \ + postgresql-client \ + && rm -rf /var/lib/apt/lists/* + +# Set working directory +WORKDIR /app + +# Copy requirements +COPY requirements.txt . + +# Install Python dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY . . + +# Create data directory +RUN mkdir -p /app/data + +# Set environment +ENV PYTHONUNBUFFERED=1 + +# Expose port +EXPOSE 5000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:5000/ || exit 1 + +# Start Flask application +CMD ["python", "app.py"] diff --git a/Package-Templates/python-app/start.sh.template b/Package-Templates/python-app/start.sh.template new file mode 100644 index 0000000..0a22a5f --- /dev/null +++ b/Package-Templates/python-app/start.sh.template @@ -0,0 +1,59 @@ +#!/bin/bash + +set -e + +# Cloudron PostgreSQL connection +DB_NAME=${CLOUDRON_POSTGRESQL_DATABASE:-app} +DB_USER=${CLOUDRON_POSTGRESQL_USERNAME:-app} +DB_PASSWORD=${CLOUDRON_POSTGRESQL_PASSWORD} +DB_HOST=${CLOUDRON_POSTGRESQL_HOST:-127.0.0.1} +DB_PORT=${CLOUDRON_POSTGRESQL_PORT:-5432} + +echo "Database host: $DB_HOST" +echo "Database port: $DB_PORT" +echo "Database name: $DB_NAME" + +# Django configuration +export DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE:-config.production} +export SECRET_KEY=${SECRET_KEY:-cloudron-secret-key-change-in-production} +export ALLOWED_HOSTS=${ALLOWED_HOSTS:-'*'} + +# Database URL (for non-Django apps) +export DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME} + +# Wait for PostgreSQL to be ready +echo "Waiting for PostgreSQL to be ready..." +until PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c '\q' 2>/dev/null; do + echo "PostgreSQL is unavailable - sleeping" + sleep 2 +done + +echo "PostgreSQL is ready!" + +# Run Django migrations (if Django) +if [ -f "manage.py" ]; then + echo "Running Django migrations..." + python manage.py migrate --noinput + + echo "Collecting static files..." + python manage.py collectstatic --noinput + + # Create admin user if specified + if [ -n "$ADMIN_USERNAME" ] && [ -n "$ADMIN_PASSWORD" ] && [ -n "$ADMIN_EMAIL" ]; then + echo "Creating admin user..." + echo "from django.contrib.auth import get_user_model; from django.core.management import call_command; User = get_user_model(); User.objects.create_superuser('$ADMIN_USERNAME', '$ADMIN_EMAIL', '$ADMIN_PASSWORD') if not User.objects.filter(email='$ADMIN_EMAIL').exists() else None" | \ + python manage.py shell 2>/dev/null || echo "Admin user may already exist" + fi +fi + +# Start application (modify as needed) +if [ -f "wsgi.py" ] || [ -f "config/wsgi.py" ]; then + echo "Starting Django with gunicorn..." + exec gunicorn config.wsgi:application --bind 0.0.0.0:5000 --workers ${NUM_WORKERS:-4} +elif [ -f "app.py" ]; then + echo "Starting Flask application..." + exec python app.py +else + echo "No entrypoint found, trying default..." + exec "$@" +fi