#!/bin/bash # Parallel Docker Build Script for Cloudron Packages # Builds multiple applications simultaneously to speed up the process set -e WORKSPACE="/home/localuser/TSYSDevStack/Cloudron/CloudronPackages-Workspace" LOG_FILE="/home/localuser/TSYSDevStack/Cloudron/parallel-build.log" MAX_PARALLEL=4 # Build 4 apps at once # Group apps by language for better parallelization GO_APPS=( "goalert" "webhook" "chirpstack" "database-gateway" "easy-gate" "fleet" "gophish" "mendersoftware" "signoz" "tirreno" ) NODE_APPS=( "runme" "autobom" "comply" "docker-drawio" "fonoster" "fx" "grist-core" "jamovi" "langfuse" "midday" "no-code-architects-toolkit" "openblocks" "PLMore" "policies" "puter" "security-awareness-training" "windmill" "wireviz-web" ) PYTHON_APPS=( "netbox" "boinc" "datahub" "docassemble" "healthchecks" "InvenTree" "mender" "nautilus_trader" "reviewboard" "satnogs" "sdrangel" "slurm" "SniperPhish" "WireViz" "sentry" ) JAVA_APPS=( "rundeck" "openboxes" "PayrollEngine" "seatunnel" ) RUST_APPS=( "hyperswitch" "rathole" "warp" ) PHP_APPS=( "corteza" "elabftw" "oat-sa" "pimcore" ) RUBY_APPS=( "huginn" "consuldemocracy" ) OTHER_APPS=( "apisix" # lua ) # Function to create minimal source files create_minimal_source() { local app_name="$1" local app_type="$2" local app_dir="$WORKSPACE/$app_name" echo " 📁 Creating minimal source for $app_name ($app_type)..." case "$app_type" in "go") cat > "$app_dir/go.mod" << EOF module github.com/tsysdevstack/$app_name go 1.21 EOF mkdir -p "$app_dir/cmd/main" cat > "$app_dir/cmd/main/main.go" << 'EOF' package main import ( "fmt" "log" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go app") }) fmt.Println("Server starting on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } EOF ;; "node") cat > "$app_dir/package.json" << EOF { "name": "$app_name", "version": "1.0.0", "main": "index.js", "scripts": { "start": "node index.js" }, "dependencies": { "express": "^4.18.0" } } EOF cat > "$app_dir/index.js" << 'EOF' const express = require('express'); const app = express(); const port = 3000; app.get('/', (req, res) => { res.send('Hello from Node.js app!'); }); app.listen(port, () => { console.log(`Server running on port ${port}`); }); EOF ;; "python") cat > "$app_dir/requirements.txt" << EOF flask==2.3.0 EOF cat > "$app_dir/app.py" << 'EOF' from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello from Python Flask app!' if __name__ == '__main__': app.run(host='0.0.0.0', port=8000) EOF ;; "java") mkdir -p "$app_dir/src/main/java/com/tsysdevstack" cat > "$app_dir/pom.xml" << EOF 4.0.0 com.tsysdevstack $app_name 1.0.0 17 17 org.springframework.boot spring-boot-starter-web 3.1.0 EOF cat > "$app_dir/src/main/java/com/tsysdevstack/Application.java" << EOF package com.tsysdevstack; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @GetMapping("/") public String home() { return "Hello from Java Spring Boot app!"; } } EOF ;; "rust") cat > "$app_dir/Cargo.toml" << EOF [package] name = "$app_name" version = "0.1.0" edition = "2021" [dependencies] tokio = { version = "1.0", features = ["full"] } EOF mkdir -p "$app_dir/src" cat > "$app_dir/src/main.rs" << 'EOF' use std::net::SocketAddr; use axum::{ response::Html, routing::get, Router, }; #[tokio::main] async fn main() { let app = Router::new().route("/", get(handler)); let addr = SocketAddr::from(([0, 0, 0, 0], 8080)); println!("listening on {}", addr); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); } async fn handler() -> Html<&'static str> { Html("

Hello from Rust!

") } EOF ;; "php") cat > "$app_dir/composer.json" << EOF { "name": "tsysdevstack/$app_name", "require": { "php": "^8.0" } } EOF cat > "$app_dir/index.php" << 'EOF' EOF ;; "ruby") cat > "$app_dir/Gemfile" << EOF source 'https://rubygems.org' gem 'sinatra' gem 'puma' EOF cat > "$app_dir/config.ru" << 'EOF' require './app.rb' run Sinatra::Application EOF cat > "$app_dir/app.rb" << 'EOF' require 'sinatra' get '/' do 'Hello from Ruby Sinatra app!' end EOF ;; "lua") cat > "$app_dir/app.lua" << 'EOF' local http = require("socket.http") print("Hello from Lua app!") EOF ;; esac } # Function to build a single application build_app() { local app_name="$1" local app_type="$2" local app_dir="$WORKSPACE/$app_name" echo "🐳 Building $app_name ($app_type)..." >> "$LOG_FILE" if [ ! -d "$app_dir/app" ]; then echo " ❌ No app directory for $app_name" >> "$LOG_FILE" return 1 fi # Create minimal source if needed if [ ! -f "$app_dir/go.mod" ] && [ ! -f "$app_dir/package.json" ] && [ ! -f "$app_dir/requirements.txt" ] && [ ! -f "$app_dir/pom.xml" ] && [ ! -f "$app_dir/Cargo.toml" ] && [ ! -f "$app_dir/composer.json" ] && [ ! -f "$app_dir/Gemfile" ]; then create_minimal_source "$app_name" "$app_type" fi cd "$app_dir/app" # Build with correct prefix local image_name="tsysdevstack-cloudron/$app_name:latest" echo " 🔨 Building $image_name..." >> "$LOG_FILE" if timeout 600 docker build -t "$image_name" . >> "$LOG_FILE" 2>&1; then echo " ✅ Build successful for $app_name" >> "$LOG_FILE" echo "✅ $app_name" return 0 else echo " ❌ Build failed for $app_name" >> "$LOG_FILE" echo "❌ $app_name" return 1 fi } # Export function for parallel execution export -f build_app create_minimal_source export WORKSPACE LOG_FILE # Main execution echo "🚀 Starting PARALLEL Docker build process..." echo "📁 Workspace: $WORKSPACE" echo "📝 Log file: $LOG_FILE" echo "🔀 Max parallel builds: $MAX_PARALLEL" echo "" # Initialize log file echo "Cloudron Packages PARALLEL Build Log - $(date)" > "$LOG_FILE" echo "===============================================" >> "$LOG_FILE" # Check Docker if ! command -v docker &> /dev/null; then echo "❌ Docker is not available" exit 1 fi start_time=$(date +%s) total_apps=0 # Count total apps total_apps=$((${#GO_APPS[@]} + ${#NODE_APPS[@]} + ${#PYTHON_APPS[@]} + ${#JAVA_APPS[@]} + ${#RUST_APPS[@]} + ${#PHP_APPS[@]} + ${#RUBY_APPS[@]} + ${#OTHER_APPS[@]})) echo "📊 Building $total_apps applications in parallel..." echo "" # Build Go apps echo "🔧 Building Go applications..." printf '%s\n' "${GO_APPS[@]}" | xargs -I {} -P $MAX_PARALLEL bash -c 'build_app "{}" "go"' echo "" # Build Node.js apps echo "🟢 Building Node.js applications..." printf '%s\n' "${NODE_APPS[@]}" | xargs -I {} -P $MAX_PARALLEL bash -c 'build_app "{}" "node"' echo "" # Build Python apps echo "🐍 Building Python applications..." printf '%s\n' "${PYTHON_APPS[@]}" | xargs -I {} -P $MAX_PARALLEL bash -c 'build_app "{}" "python"' echo "" # Build Java apps echo "☕ Building Java applications..." printf '%s\n' "${JAVA_APPS[@]}" | xargs -I {} -P $MAX_PARALLEL bash -c 'build_app "{}" "java"' echo "" # Build Rust apps echo "🦀 Building Rust applications..." printf '%s\n' "${RUST_APPS[@]}" | xargs -I {} -P $MAX_PARALLEL bash -c 'build_app "{}" "rust"' echo "" # Build PHP apps echo "🐘 Building PHP applications..." printf '%s\n' "${PHP_APPS[@]}" | xargs -I {} -P $MAX_PARALLEL bash -c 'build_app "{}" "php"' echo "" # Build Ruby apps echo "💎 Building Ruby applications..." printf '%s\n' "${RUBY_APPS[@]}" | xargs -I {} -P $MAX_PARALLEL bash -c 'build_app "{}" "ruby"' echo "" # Build other apps echo "🔮 Building other applications..." printf '%s\n' "${OTHER_APPS[@]}" | xargs -I {} -P $MAX_PARALLEL bash -c 'build_app "{}" "lua"' echo "" end_time=$(date +%s) duration=$((end_time - start_time)) # Count successful builds success_count=$(docker images | grep tsysdevstack-cloudron | wc -l) echo "🎉 PARALLEL build process complete!" echo "📊 Results: $success_count/$total_apps builds successful" echo "⏱️ Duration: ${duration} seconds" echo "" echo "📋 Built images:" docker images | grep tsysdevstack-cloudron if [ "$success_count" -eq "$total_apps" ]; then echo "✅ All applications built successfully!" else echo "⚠️ Some builds failed - check $LOG_FILE for details" fi