#!/bin/bash # Function library for Cloudron packaging # Contains specific packaging functions for different application types set -e # Exit on any error # Function to package generic Node.js application package_nodejs_app() { local app_name=$1 local app_dir=$2 local artifact_dir=$3 cd "$app_dir" # Create Cloudron manifest cat > app.manifest << EOF { "id": "com.$(echo "$app_name" | sed 's/[^a-zA-Z0-9]/./g').cloudron", "title": "$app_name", "version": "1.0.0", "build": "1", "description": "Cloudron package for $app_name", "author": "Auto-generated", "website": "https://github.com/$app_name", "admin": false, "tags": ["nodejs", "auto-generated"], "logo": "https://github.com/fluidicon.png", "documentation": "https://github.com/$app_name", "changelog": "Initial packaging" } EOF # Create Dockerfile for Node.js cat > Dockerfile << EOF FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm install --only=production COPY . . EXPOSE 3000 CMD ["npm", "start"] EOF # Build Docker image local docker_image="tsysdevstack-cloudron-buildtest-${app_name//[^a-zA-Z0-9]/-}:latest" if ! docker build -t "$docker_image" .; then echo "Failed to build Docker image for $app_name" return 1 fi # Perform smoke test on the Docker image if ! smoke_test_docker_image "$docker_image" "$app_name"; then echo "Smoke test failed for $app_name" return 1 fi # Save the Docker image as an artifact docker save "$docker_image" | gzip > "$artifact_dir/${app_name//[^a-zA-Z0-9]/-}.tar.gz" return 0 } # Function to package generic Python application package_python_app() { local app_name=$1 local app_dir=$2 local artifact_dir=$3 cd "$app_dir" # Create Cloudron manifest cat > app.manifest << EOF { "id": "com.$(echo "$app_name" | sed 's/[^a-zA-Z0-9]/./g').cloudron", "title": "$app_name", "version": "1.0.0", "build": "1", "description": "Cloudron package for $app_name", "author": "Auto-generated", "website": "https://github.com/$app_name", "admin": false, "tags": ["python", "auto-generated"], "logo": "https://github.com/fluidicon.png", "documentation": "https://github.com/$app_name", "changelog": "Initial packaging" } EOF # Create Dockerfile for Python cat > Dockerfile << EOF FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD ["python", "app.py"] EOF # Build Docker image local docker_image="tsysdevstack-cloudron-buildtest-${app_name//[^a-zA-Z0-9]/-}:latest" if ! docker build -t "$docker_image" .; then echo "Failed to build Docker image for $app_name" return 1 fi # Perform smoke test on the Docker image if ! smoke_test_docker_image "$docker_image" "$app_name"; then echo "Smoke test failed for $app_name" return 1 fi # Save the Docker image as an artifact docker save "$docker_image" | gzip > "$artifact_dir/${app_name//[^a-zA-Z0-9]/-}.tar.gz" return 0 } # Function to package generic PHP application package_php_app() { local app_name=$1 local app_dir=$2 local artifact_dir=$3 cd "$app_dir" # Create Cloudron manifest cat > app.manifest << EOF { "id": "com.$(echo "$app_name" | sed 's/[^a-zA-Z0-9]/./g').cloudron", "title": "$app_name", "version": "1.0.0", "build": "1", "description": "Cloudron package for $app_name", "author": "Auto-generated", "website": "https://github.com/$app_name", "admin": false, "tags": ["php", "auto-generated"], "logo": "https://github.com/fluidicon.png", "documentation": "https://github.com/$app_name", "changelog": "Initial packaging" } EOF # Create Dockerfile for PHP cat > Dockerfile << EOF FROM php:8.1-apache RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli COPY . /var/www/html/ EXPOSE 80 CMD ["apache2-foreground"] EOF # Build Docker image local docker_image="tsysdevstack-cloudron-buildtest-${app_name//[^a-zA-Z0-9]/-}:latest" if ! docker build -t "$docker_image" .; then echo "Failed to build Docker image for $app_name" return 1 fi # Perform smoke test on the Docker image if ! smoke_test_docker_image "$docker_image" "$app_name"; then echo "Smoke test failed for $app_name" return 1 fi # Save the Docker image as an artifact docker save "$docker_image" | gzip > "$artifact_dir/${app_name//[^a-zA-Z0-9]/-}.tar.gz" return 0 } # Function to package generic Go application package_go_app() { local app_name=$1 local app_dir=$2 local artifact_dir=$3 cd "$app_dir" # Create Cloudron manifest cat > app.manifest << EOF { "id": "com.$(echo "$app_name" | sed 's/[^a-zA-Z0-9]/./g').cloudron", "title": "$app_name", "version": "1.0.0", "build": "1", "description": "Cloudron package for $app_name", "author": "Auto-generated", "website": "https://github.com/$app_name", "admin": false, "tags": ["go", "auto-generated"], "logo": "https://github.com/fluidicon.png", "documentation": "https://github.com/$app_name", "changelog": "Initial packaging" } EOF # Create Dockerfile for Go cat > Dockerfile << EOF FROM golang:1.21-alpine AS builder WORKDIR /app COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o myapp . FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/myapp . EXPOSE 8080 CMD ["./myapp"] EOF # Build Docker image local docker_image="tsysdevstack-cloudron-buildtest-${app_name//[^a-zA-Z0-9]/-}:latest" if ! docker build -t "$docker_image" .; then echo "Failed to build Docker image for $app_name" return 1 fi # Perform smoke test on the Docker image if ! smoke_test_docker_image "$docker_image" "$app_name"; then echo "Smoke test failed for $app_name" return 1 fi # Save the Docker image as an artifact docker save "$docker_image" | gzip > "$artifact_dir/${app_name//[^a-zA-Z0-9]/-}.tar.gz" return 0 } # Function to perform smoke test on Docker images smoke_test_docker_image() { local docker_image=$1 local app_name=$2 echo "Performing smoke test on $docker_image for $app_name" # Run the container briefly to test if it starts correctly local container_name="smoke-test-${app_name//[^a-zA-Z0-9]/-}-$(date +%s)" # Try to run the container and check if it starts without immediate failure if docker run -d --name "$container_name" --health-cmd="curl -f http://localhost/ || exit 1" --health-interval=5s --health-timeout=3s --health-retries=3 "$docker_image"; then # Wait a few seconds to see if the container stays running sleep 10 # Check if the container is still running and healthy if [ "$(docker inspect -f '{{.State.Status}}' "$container_name" 2>/dev/null)" = "running" ]; then echo "Smoke test passed for $app_name" # Stop and remove the test container docker stop "$container_name" > /dev/null 2>&1 docker rm "$container_name" > /dev/null 2>&1 return 0 else echo "Container for $app_name did not stay running during smoke test" # Get logs for debugging docker logs "$container_name" 2>&1 | head -20 docker stop "$container_name" > /dev/null 2>&1 docker rm "$container_name" > /dev/null 2>&1 return 1 fi else echo "Failed to start container for $app_name during smoke test" docker rm "$container_name" > /dev/null 2>&1 return 1 fi } # Generic function that detects application type and calls appropriate function detect_and_package() { local app_name=$1 local app_dir=$2 local artifact_dir=$3 cd "$app_dir" # Detect application type based on files if [ -f "package.json" ]; then echo "Detected Node.js application" package_nodejs_app "$app_name" "$app_dir" "$artifact_dir" elif [ -f "requirements.txt" ] || [ -f "setup.py" ]; then echo "Detected Python application" package_python_app "$app_name" "$app_dir" "$artifact_dir" elif [ -f "composer.json" ]; then echo "Detected PHP application" package_php_app "$app_name" "$app_dir" "$artifact_dir" elif [ -f "go.mod" ] || [ -f "*.go" ]; then echo "Detected Go application" package_go_app "$app_name" "$app_dir" "$artifact_dir" else # Default generic approach echo "Application type not detected, using generic approach" package_generic_app "$app_name" "$app_dir" "$artifact_dir" fi } # Generic packaging function for unknown application types package_generic_app() { local app_name=$1 local app_dir=$2 local artifact_dir=$3 cd "$app_dir" # Create Cloudron manifest cat > app.manifest << EOF { "id": "com.$(echo "$app_name" | sed 's/[^a-zA-Z0-9]/./g').cloudron", "title": "$app_name", "version": "1.0.0", "build": "1", "description": "Cloudron package for $app_name", "author": "Auto-generated", "website": "https://github.com/$app_name", "admin": false, "tags": ["generic", "auto-generated"], "logo": "https://github.com/fluidicon.png", "documentation": "https://github.com/$app_name", "changelog": "Initial packaging" } EOF # Create a basic Dockerfile cat > Dockerfile << EOF FROM alpine:latest WORKDIR /app COPY . . RUN apk add --no-cache bash curl tar EXPOSE 8080 CMD ["sh", "-c", "while true; do sleep 30; done"] EOF # Build Docker image local docker_image="tsysdevstack-cloudron-buildtest-${app_name//[^a-zA-Z0-9]/-}:latest" if ! docker build -t "$docker_image" .; then echo "Failed to build Docker image for $app_name" return 1 fi # Perform smoke test on the Docker image if ! smoke_test_docker_image "$docker_image" "$app_name"; then echo "Smoke test failed for $app_name" return 1 fi # Save the Docker image as an artifact docker save "$docker_image" | gzip > "$artifact_dir/${app_name//[^a-zA-Z0-9]/-}.tar.gz" return 0 }