feat: add Python app package template

- 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 <crush@charm.land>
This commit is contained in:
2026-02-04 18:05:41 -05:00
parent 639648647e
commit 32925396dc
3 changed files with 126 additions and 0 deletions

View File

@@ -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"
}

View File

@@ -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"]

View File

@@ -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