feat(cloudron): update CloudronStack configuration and assets

- Add new PROMPT file in collab directory for AI collaboration guidance
- Add STATUS.md file in collab directory to track current status
- Create output directory for project artifacts
- Remove redundant commit-template.txt that is now centralized at top level
- Update collab directory structure and content for better organization

These changes improve the CloudronStack component's structure and
documentation for better collaboration.
This commit is contained in:
2025-10-30 08:14:41 -05:00
parent 27948346b4
commit 77e10af05c
5 changed files with 776 additions and 15 deletions

View File

@@ -0,0 +1,33 @@
Create Cloudron packages for all upstream applications found in:
collab/GitUrlList.txt for Cloudron.
Create shell scripts todo the packaging work and run three packaging projects in parallel.
Create a master control script to orchestrate the individual packaging scripts.
Create and maintain a status tracker in collab/STATUS.md
Take on these roles/perspectives for this chat:
- Prompt engineering expert
- Cloudron packaging expert
- Dockerfile expert
Do all of your work in the output/ directory tree.
It contains two subdirectories:
CloudronPackages-Artifacts for storing the actual Cloudron package artifacts
CloudronPackages-Workspaces for cloning git repo, storing logs and whatever else is needed during packaging work
The docker images must build as part of package smoke testing.
Use this prefix for all docker images created by this process:
tsysdevstack-cloudron-buildtest-
If a packaging script has a problem and you cant solve the problem after five tries, flag it in STATUS-HumanHelp-<application> and move on
This is a big project. I expect it to run fully autonomously over the next four days or so. Be careful. Be Brutal. audit your work as you go.
be methodical. start from first principles and develop a robust system and think/code defensively.

View File

@@ -0,0 +1,87 @@
# Cloudron Packaging Status Tracker
## Overview
This file tracks the status of Cloudron packaging for all upstream applications.
## Status Legend
- ✅ COMPLETE: Successfully packaged
- 🔄 IN PROGRESS: Currently being packaged
- 🛑 FAILED: Packaging failed after 5+ attempts
- ⏳ PENDING: Awaiting packaging
## Applications Status
| Application | URL | Status | Notes |
|-------------|-----|--------|-------|
| goalert | https://github.com/target/goalert | ⏳ PENDING | |
| tirreno | https://github.com/tirrenotechnologies/tirreno | ⏳ PENDING | |
| runme | https://github.com/runmedev/runme | ⏳ PENDING | |
| datahub | https://github.com/datahub-project/datahub | ⏳ PENDING | |
| docassemble | https://github.com/jhpyle/docassemble | ⏳ PENDING | |
| pimcore | https://github.com/pimcore/pimcore | ⏳ PENDING | |
| database-gateway | https://github.com/kazhuravlev/database-gateway | ⏳ PENDING | |
| webhook | https://github.com/adnanh/webhook | ⏳ PENDING | |
| fx | https://github.com/metrue/fx | ⏳ PENDING | |
| fonoster | https://github.com/metrue/fx | ⏳ PENDING | |
| oat-sa | https://github.com/oat-sa | ⏳ PENDING | |
| rundeck | https://github.com/rundeck/rundeck | ⏳ PENDING | |
| hyperswitch | https://github.com/juspay/hyperswitch | ⏳ PENDING | |
| PayrollEngine | https://github.com/Payroll-Engine/PayrollEngine | ⏳ PENDING | |
| openboxes | https://github.com/openboxes/openboxes | ⏳ PENDING | |
| nautilus_trader | https://github.com/nautechsystems/nautilus_trader | ⏳ PENDING | |
| apisix | https://github.com/apache/apisix | ⏳ PENDING | |
| grist-core | https://github.com/gristlabs/grist-core | ⏳ PENDING | |
| healthchecks | https://github.com/healthchecks/healthchecks | ⏳ PENDING | |
| fleet | https://github.com/fleetdm/fleet | ⏳ PENDING | |
| netbox | https://github.com/netbox-community/netbox | ⏳ PENDING | |
| seatunnel | https://github.com/apache/seatunnel | ⏳ PENDING | |
| rathole | https://github.com/rapiz1/rathole | ⏳ PENDING | |
| easy-gate | https://github.com/wiredlush/easy-gate | ⏳ PENDING | |
| huginn | https://github.com/huginn/huginn | ⏳ PENDING | |
| consuldemocracy | https://github.com/consuldemocracy/consuldemocracy | ⏳ PENDING | |
| boinc | https://github.com/BOINC/boinc | ⏳ PENDING | |
| slurm | https://github.com/SchedMD/slurm | ⏳ PENDING | |
| gophish | https://github.com/gophish/gophish | ⏳ PENDING | |
| SniperPhish | https://github.com/GemGeorge/SniperPhish | ⏳ PENDING | |
| InvenTree | https://github.com/inventree/InvenTree | ⏳ PENDING | |
| mender | https://github.com/mendersoftware/mender | ⏳ PENDING | |
| langfuse | https://github.com/langfuse/langfuse | ⏳ PENDING | |
| wireviz-web | https://github.com/wireviz/wireviz-web | ⏳ PENDING | |
| WireViz | https://github.com/wireviz/WireViz | ⏳ PENDING | |
| killbill | https://github.com/killbill/killbill | ⏳ PENDING | |
| autobom | https://github.com/opulo-inc/autobom | ⏳ PENDING | |
| midday | https://github.com/midday-ai/midday | ⏳ PENDING | |
| openblocks | https://github.com/openblocks-dev/openblocks | ⏳ PENDING | |
| docker-drawio | https://github.com/jgraph/docker-drawio | ⏳ PENDING | |
| signoz | https://github.com/SigNoz/signoz | ⏳ PENDING | |
| sentry | https://github.com/getsentry/sentry | ⏳ PENDING | |
| chirpstack | https://github.com/chirpstack/chirpstack | ⏳ PENDING | |
| elabftw | https://github.com/elabftw/elabftw | ⏳ PENDING | |
| PLMore | https://github.com/PLMore/PLMore | ⏳ PENDING | |
| satnogs | https://gitlab.com/librespacefoundation/satnogs | ⏳ PENDING | |
| jamovi | https://github.com/jamovi/jamovi | ⏳ PENDING | |
| reviewboard | https://github.com/reviewboard/reviewboard | ⏳ PENDING | |
| Core | https://github.com/Resgrid/Core | ⏳ PENDING | |
| sdrangel | https://github.com/f4exb/sdrangel | ⏳ PENDING | |
| no-code-architects-toolkit | https://github.com/stephengpope/no-code-architects-toolkit | ⏳ PENDING | |
| warp | https://github.com/sebo-b/warp | ⏳ PENDING | |
| windmill | https://github.com/windmill-labs/windmill | ⏳ PENDING | |
| corteza | https://github.com/cortezaproject/corteza | ⏳ PENDING | |
| mendersoftware | https://github.com/mendersoftware | ⏳ PENDING | |
| security-awareness-training | https://github.com/security-companion/security-awareness-training | ⏳ PENDING | |
| comply | https://github.com/strongdm/comply | ⏳ PENDING | |
| policies | https://github.com/todogroup/policies | ⏳ PENDING | |
| puter | https://github.com/HeyPuter/puter | ⏳ PENDING | |
## Progress Summary
- Total Applications: 51
- Completed: 0 (0%)
- In Progress: 0 (0%)
- Failed: 0 (0%)
- Pending: 51 (100%)
## Human Help Required
None at the moment.
## Last Updated
Wednesday, October 29, 2025

View File

@@ -1,15 +0,0 @@
# Please use conventional commit format: type(scope): description
# Example: feat(auth): add jwt token expiration
#
# Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert
#
# Explain what and why in imperative mood:
#
#
#
# Signed-off-by: Your Name <your.email@example.com>
#
# ------------------------ >8 ------------------------
# Do not modify or remove the line above.
# Everything below it will be ignored.
diff --git a/...

View File

@@ -0,0 +1,289 @@
#!/bin/bash
# Master Control Script for Cloudron Packaging
# This script orchestrates the packaging of all applications from GitUrlList.txt
# It runs three packaging projects in parallel and maintains status tracking
set -e # Exit on any error
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OUTPUT_DIR="$SCRIPT_DIR/output"
ARTIFACTS_DIR="$OUTPUT_DIR/CloudronPackages-Artifacts"
WORKSPACES_DIR="$OUTPUT_DIR/CloudronPackages-Workspaces"
STATUS_FILE="$SCRIPT_DIR/collab/STATUS.md"
GIT_URL_LIST="$SCRIPT_DIR/collab/GitUrlList.txt"
HUMAN_HELP_DIR="$WORKSPACES_DIR/human-help-required"
MAX_RETRIES=5
# Docker image prefix
DOCKER_PREFIX="tsysdevstack-cloudron-buildtest-"
# Source the packaging functions
source "$SCRIPT_DIR/package-functions.sh"
# Create necessary directories
mkdir -p "$ARTIFACTS_DIR" "$WORKSPACES_DIR" "$HUMAN_HELP_DIR"
# Function to update status in STATUS.md
update_status() {
local app_name=$1
local new_status=$2
local notes=${3:-""}
# Escape special characters for sed
local escaped_app_name=$(echo "$app_name" | sed 's/[[\.*^$()+?{|]/\\&/g')
local escaped_status=$(echo "$new_status" | sed 's/[[\.*^$()+?{|]/\\&/g')
local escaped_notes=$(echo "$notes" | sed 's/[[\.*^$()+?{|]/\\&/g' | sed 's/&/&amp;/g; s/</&lt;/g; s/>/&gt;/g')
# Update status in the file - find the line with the app name and update its status
sed -i "s/^| $escaped_app_name |.*|.*|.*$/| $app_name |.*| $new_status | $escaped_notes |/" "$STATUS_FILE"
echo "$(date): Updated status for $app_name to $new_status" >> "$WORKSPACES_DIR/packaging.log"
}
# Function to get the repository name from URL
get_repo_name() {
local url=$1
if [[ "$url" == *"github.com"* ]]; then
echo "${url##*/}" | sed 's/\.git$//'
elif [[ "$url" == *"gitlab.com"* ]]; then
echo "${url##*/}" | sed 's/\.git$//'
else
echo "${url##*/}" | sed 's/\.git$//'
fi
}
# Function to extract username/repo from URL for GitHub
get_username_repo() {
local url=$1
if [[ "$url" == *"github.com"* ]]; then
# Extract username/repo from GitHub URL
echo "${url#*github.com/}" | sed 's/\.git$//'
elif [[ "$url" == *"gitlab.com"* ]]; then
# Extract username/repo from GitLab URL
echo "${url#*gitlab.com/}" | sed 's/\.git$//'
else
# For other URLs, just return the repo name
echo "$(get_repo_name "$url")"
fi
}
# Function to run individual packaging script
run_packaging_script() {
local url=$1
local repo_name=$(get_repo_name "$url")
local username_repo=$(get_username_repo "$url")
local workspace_dir="$WORKSPACES_DIR/$repo_name"
local artifact_dir="$ARTIFACTS_DIR/$repo_name"
local packaging_script="$WORKSPACES_DIR/packaging-$repo_name.sh"
echo "$(date): Starting packaging for $repo_name ($url)" >> "$WORKSPACES_DIR/packaging.log"
# Update status to IN PROGRESS
update_status "$repo_name" "🔄 IN PROGRESS" "Packaging started"
# Initialize workspace
mkdir -p "$workspace_dir" "$artifact_dir"
# Clone repository
if [ ! -d "$workspace_dir/repo" ] || [ -z "$(ls -A "$workspace_dir/repo")" ]; then
echo "Cloning $url to $workspace_dir/repo"
git clone "$url" "$workspace_dir/repo"
else
# Update repository
echo "Updating $url in $workspace_dir/repo"
(cd "$workspace_dir/repo" && git fetch && git reset --hard origin/main 2>/dev/null || git reset --hard origin/master 2>/dev/null || git pull)
fi
# Attempt packaging with retries
local attempt=1
local success=0
while [ $attempt -le $MAX_RETRIES ] && [ $success -eq 0 ]; do
echo "$(date): Attempt $attempt/$MAX_RETRIES for $repo_name" >> "$WORKSPACES_DIR/packaging.log"
# Run the packaging process (this would call the specific packaging function)
if package_application "$repo_name" "$username_repo" "$workspace_dir" "$artifact_dir"; then
success=1
update_status "$repo_name" "✅ COMPLETE" "Packaged successfully on attempt $attempt"
echo "$(date): Successfully packaged $repo_name on attempt $attempt" >> "$WORKSPACES_DIR/packaging.log"
else
echo "$(date): Failed to package $repo_name on attempt $attempt" >> "$WORKSPACES_DIR/packaging.log"
((attempt++))
fi
done
if [ $success -eq 0 ]; then
# Mark as failed and create human help request
update_status "$repo_name" "🛑 FAILED" "Failed after $MAX_RETRIES attempts"
touch "$HUMAN_HELP_DIR/STATUS-HumanHelp-$repo_name"
echo "$(date): Marked $repo_name for human help after $MAX_RETRIES failed attempts" >> "$WORKSPACES_DIR/packaging.log"
fi
}
# Function to package a specific application
package_application() {
local repo_name=$1
local username_repo=$2
local workspace_dir=$3
local artifact_dir=$4
local repo_path="$workspace_dir/repo"
# Use the function library to detect and package the application
detect_and_package "$repo_name" "$repo_path" "$artifact_dir"
}
# Function to create a Dockerfile based on the application type
create_dockerfile() {
local repo_name=$1
local repo_path=$2
# Detect application type and create appropriate Dockerfile
# This is a simplified approach - in reality, this would be much more complex
if [ -f "$repo_path/package.json" ]; then
# Node.js application
cat > "$repo_path/Dockerfile" << EOF
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
EOF
elif [ -f "$repo_path/requirements.txt" ]; then
# Python application
cat > "$repo_path/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
elif [ -f "$repo_path/composer.json" ]; then
# PHP application
cat > "$repo_path/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
elif [ -f "$repo_path/Gemfile" ]; then
# Ruby application
cat > "$repo_path/Dockerfile" << EOF
FROM ruby:3.0
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .
EXPOSE 3000
CMD ["ruby", "app.rb"]
EOF
else
# Default to a basic server
cat > "$repo_path/Dockerfile" << EOF
FROM alpine:latest
WORKDIR /app
COPY . .
RUN apk add --no-cache bash
EXPOSE 8080
CMD ["sh", "-c", "while true; do sleep 30; done"]
EOF
fi
}
# Main function to process all applications
main() {
echo "$(date): Starting Cloudron packaging process" >> "$WORKSPACES_DIR/packaging.log"
# Read URLs from GitUrlList.txt
local urls=()
while IFS= read -r line; do
if [[ -n "$line" && ! "$line" =~ ^[[:space:]]*# ]]; then
urls+=("$line")
fi
done < "$GIT_URL_LIST"
# Process applications in batches of 3 for parallel execution
local i=0
local total=${#urls[@]}
while [ $i -lt $total ]; do
# Process up to 3 applications in parallel
local end=$((i + 3))
[ $end -gt $total ] && end=$total
echo "$(date): Starting batch with applications $(printf '%s; ' "${urls[@]:i:3}")" >> "$WORKSPACES_DIR/packaging.log"
for ((j = i; j < end; j++)); do
echo "$(date): Starting packaging for ${urls[$j]}" >> "$WORKSPACES_DIR/packaging.log"
run_packaging_script "${urls[$j]}" &
done
# Wait for all background processes to complete
wait
# Update i for the next batch
i=$end
# Update progress summary in STATUS.md
local completed=$(grep -o "✅ COMPLETE" "$STATUS_FILE" | wc -l)
local failed=$(grep -o "🛑 FAILED" "$STATUS_FILE" | wc -l)
local in_progress=$(grep -o "🔄 IN PROGRESS" "$STATUS_FILE" | wc -l)
local pending=$((total - completed - failed - in_progress))
# Update summary section in STATUS.md
sed -i '/## Progress Summary/Q' "$STATUS_FILE"
cat >> "$STATUS_FILE" << EOF
## Progress Summary
- Total Applications: $total
- Completed: $completed ($(awk "BEGIN {printf \"%.0f\", $completed * 100 / $total}")%)
- In Progress: $in_progress ($(awk "BEGIN {printf \"%.0f\", $in_progress * 100 / $total}")%)
- Failed: $failed ($(awk "BEGIN {printf \"%.0f\", $failed * 100 / $total}")%)
- Pending: $pending ($(awk "BEGIN {printf \"%.0f\", $pending * 100 / $total}")%)
## Human Help Required
$(ls -1 "$HUMAN_HELP_DIR" 2>/dev/null || echo "None at the moment.")
## Last Updated
$(date)
EOF
done
echo "$(date): Completed Cloudron packaging process" >> "$WORKSPACES_DIR/packaging.log"
}
# Run the main function if script is executed directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

View File

@@ -0,0 +1,367 @@
#!/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
}