From 77e10af05c5959f1adc352749fb98bb212f2969c Mon Sep 17 00:00:00 2001 From: ReachableCEO Date: Thu, 30 Oct 2025 08:14:41 -0500 Subject: [PATCH] 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. --- CloudronStack/collab/PROMPT | 33 ++ CloudronStack/collab/STATUS.md | 87 +++++ CloudronStack/commit-template.txt | 15 - CloudronStack/output/master-control-script.sh | 289 ++++++++++++++ CloudronStack/output/package-functions.sh | 367 ++++++++++++++++++ 5 files changed, 776 insertions(+), 15 deletions(-) create mode 100644 CloudronStack/collab/PROMPT create mode 100644 CloudronStack/collab/STATUS.md delete mode 100644 CloudronStack/commit-template.txt create mode 100755 CloudronStack/output/master-control-script.sh create mode 100644 CloudronStack/output/package-functions.sh diff --git a/CloudronStack/collab/PROMPT b/CloudronStack/collab/PROMPT new file mode 100644 index 0000000..a66197c --- /dev/null +++ b/CloudronStack/collab/PROMPT @@ -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- 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. \ No newline at end of file diff --git a/CloudronStack/collab/STATUS.md b/CloudronStack/collab/STATUS.md new file mode 100644 index 0000000..9da7900 --- /dev/null +++ b/CloudronStack/collab/STATUS.md @@ -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 \ No newline at end of file diff --git a/CloudronStack/commit-template.txt b/CloudronStack/commit-template.txt deleted file mode 100644 index 05f1e6b..0000000 --- a/CloudronStack/commit-template.txt +++ /dev/null @@ -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 -# -# ------------------------ >8 ------------------------ -# Do not modify or remove the line above. -# Everything below it will be ignored. -diff --git a/... \ No newline at end of file diff --git a/CloudronStack/output/master-control-script.sh b/CloudronStack/output/master-control-script.sh new file mode 100755 index 0000000..d143df4 --- /dev/null +++ b/CloudronStack/output/master-control-script.sh @@ -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/&/&/g; s//>/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 \ No newline at end of file diff --git a/CloudronStack/output/package-functions.sh b/CloudronStack/output/package-functions.sh new file mode 100644 index 0000000..51bd3f9 --- /dev/null +++ b/CloudronStack/output/package-functions.sh @@ -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 +} \ No newline at end of file