Compare commits

..

2 Commits

Author SHA1 Message Date
dd474374d4 feat(toolbox): update Docker configuration and documentation
- Update ToolboxStack/output/toolbox-base/Dockerfile with latest container configurations
- Update ToolboxStack/output/toolbox-base/PROMPT with enhanced AI collaboration instructions
- Update ToolboxStack/output/toolbox-base/README.md with current documentation
- Add ToolboxStack/collab/tool-additions/ directory for additional tool configurations
- Update CloudronStack/output/master-control-script.sh with improved automation logic

These changes enhance the developer workspace configuration and
improve automation workflows across the project.
2025-10-30 08:16:06 -05:00
77e10af05c 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.
2025-10-30 08:14:41 -05:00
9 changed files with 863 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,355 @@
#!/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
set -u # Exit on undefined variables
set -o pipefail # Exit on pipe failures
# 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
LOG_FILE="$WORKSPACES_DIR/packaging.log"
# 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 log messages
log_message() {
local level=$1
local message=$2
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
}
# Function to perform audit of the packaging process
perform_audit() {
log_message "INFO" "Starting audit process"
# Count total, completed, failed, and in-progress applications
local total_count=$(grep -c "https://" "$GIT_URL_LIST" || echo 0)
local completed_count=$(grep -c "✅ COMPLETE" "$STATUS_FILE" || echo 0)
local failed_count=$(grep -c "🛑 FAILED" "$STATUS_FILE" || echo 0)
local in_progress_count=$(grep -c "🔄 IN PROGRESS" "$STATUS_FILE" || echo 0)
local pending_count=$((total_count - completed_count - failed_count - in_progress_count))
log_message "INFO" "Audit Summary - Total: $total_count, Completed: $completed_count, Failed: $failed_count, In Progress: $in_progress_count, Pending: $pending_count"
# Check for artifacts directory health
local artifact_count=$(find "$ARTIFACTS_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
log_message "INFO" "Found $artifact_count artifact directories in $ARTIFACTS_DIR"
# Check for workspace directory health
local workspace_count=$(find "$WORKSPACES_DIR" -mindepth 1 -maxdepth 1 -type d | grep -v "human-help-required\|packaging.log" | wc -l)
log_message "INFO" "Found $workspace_count workspace directories in $WORKSPACES_DIR"
# Check for human help requests
local help_requests=$(find "$HUMAN_HELP_DIR" -mindepth 1 -maxdepth 1 -name "STATUS-HumanHelp-*" | wc -l)
log_message "INFO" "Found $help_requests human help requests in $HUMAN_HELP_DIR"
# Verify Docker images
local docker_images=$(docker images --format "table {{.Repository}}:{{.Tag}}" | grep "$DOCKER_PREFIX" | wc -l)
log_message "INFO" "Found $docker_images Docker images with prefix $DOCKER_PREFIX"
log_message "INFO" "Audit process completed"
}
# 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"
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"
if ! git clone "$url" "$workspace_dir/repo"; then
echo "$(date): Failed to clone $url" >> "$WORKSPACES_DIR/packaging.log"
update_status "$repo_name" "🛑 FAILED" "Failed to clone repository"
return 1
fi
else
# Update repository
echo "Updating $url in $workspace_dir/repo"
if ! (cd "$workspace_dir/repo" && git fetch && git reset --hard origin/main 2>/dev/null || git reset --hard origin/master 2>/dev/null || git pull); then
echo "$(date): Failed to update $url" >> "$WORKSPACES_DIR/packaging.log"
update_status "$repo_name" "🔄 IN PROGRESS" "Repo update failed, will retry with fresh clone"
# Remove the repo and try to clone again
rm -rf "$workspace_dir/repo"
if ! git clone "$url" "$workspace_dir/repo"; then
echo "$(date): Failed to re-clone $url after update failure" >> "$WORKSPACES_DIR/packaging.log"
update_status "$repo_name" "🛑 FAILED" "Failed to update or re-clone repository"
return 1
fi
fi
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"
# Capture the output and error of the packaging function
if package_application "$repo_name" "$username_repo" "$workspace_dir" "$artifact_dir" 2>"$workspace_dir/error.log"; 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"
cat "$workspace_dir/error.log" >> "$WORKSPACES_DIR/packaging.log"
((attempt++))
fi
done
if [ $success -eq 0 ]; then
# Mark as failed and create human help request with more detailed information
local error_details=$(cat "$workspace_dir/error.log" | head -20 | sed 's/"/\\"/g' | tr '\n' ' ')
update_status "$repo_name" "🛑 FAILED" "Failed after $MAX_RETRIES attempts. Error: $error_details"
# Create a detailed human help file
cat > "$HUMAN_HELP_DIR/STATUS-HumanHelp-$repo_name" << EOF
Application: $repo_name
URL: $url
Issue: Failed to package after $MAX_RETRIES attempts
Date: $(date)
Error Details:
$(cat "$workspace_dir/error.log")
EOF
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
}

View File

@@ -0,0 +1,7 @@
I need to add the following tools to the toolbox-base image:
- https://github.com/just-every/code
- https://github.com/QwenLM/qwen-code
- https://github.com/google-gemini/gemini-cli
- https://github.com/openai/codex
- https://github.com/sst/opencode

View File

@@ -68,6 +68,9 @@ RUN curl -sSfL https://raw.githubusercontent.com/aquaproj/aqua-installer/v2.3.1/
# Install mise for runtime management (no global toolchains pre-installed) # Install mise for runtime management (no global toolchains pre-installed)
RUN curl -sSfL https://mise.jdx.dev/install.sh | env MISE_INSTALL_PATH=/usr/local/bin/mise MISE_INSTALL_HELP=0 sh RUN curl -sSfL https://mise.jdx.dev/install.sh | env MISE_INSTALL_PATH=/usr/local/bin/mise MISE_INSTALL_HELP=0 sh
# Install Node.js via mise to enable npm package installation
RUN mise install node@lts && mise global node@lts
# Create non-root user with matching UID/GID for host mapping # Create non-root user with matching UID/GID for host mapping
RUN if getent passwd "${USER_ID}" >/dev/null; then \ RUN if getent passwd "${USER_ID}" >/dev/null; then \
existing_user="$(getent passwd "${USER_ID}" | cut -d: -f1)"; \ existing_user="$(getent passwd "${USER_ID}" | cut -d: -f1)"; \
@@ -96,6 +99,9 @@ RUN su - "${USERNAME}" -c 'git clone --depth=1 https://github.com/ohmyzsh/ohmyzs
&& su - "${USERNAME}" -c 'mkdir -p ~/.config/fish' \ && su - "${USERNAME}" -c 'mkdir -p ~/.config/fish' \
&& su - "${USERNAME}" -c 'printf "\nset -gx AQUA_GLOBAL_CONFIG \$HOME/.config/aquaproj-aqua/aqua.yaml\n# Shell prompt and runtime manager\nstarship init fish | source\nmise activate fish | source\ndirenv hook fish | source\nzoxide init fish | source\n" >> ~/.config/fish/config.fish' && su - "${USERNAME}" -c 'printf "\nset -gx AQUA_GLOBAL_CONFIG \$HOME/.config/aquaproj-aqua/aqua.yaml\n# Shell prompt and runtime manager\nstarship init fish | source\nmise activate fish | source\ndirenv hook fish | source\nzoxide init fish | source\n" >> ~/.config/fish/config.fish'
# Install Node.js for the toolbox user and set up the environment
RUN su - "${USERNAME}" -c 'mise install node@lts && mise use -g node@lts'
COPY aqua.yaml /tmp/aqua.yaml COPY aqua.yaml /tmp/aqua.yaml
RUN chown "${USER_ID}:${GROUP_ID}" /tmp/aqua.yaml \ RUN chown "${USER_ID}:${GROUP_ID}" /tmp/aqua.yaml \
@@ -103,6 +109,12 @@ RUN chown "${USER_ID}:${GROUP_ID}" /tmp/aqua.yaml \
&& su - "${USERNAME}" -c 'cp /tmp/aqua.yaml ~/.config/aquaproj-aqua/aqua.yaml' \ && su - "${USERNAME}" -c 'cp /tmp/aqua.yaml ~/.config/aquaproj-aqua/aqua.yaml' \
&& su - "${USERNAME}" -c 'AQUA_GLOBAL_CONFIG=~/.config/aquaproj-aqua/aqua.yaml aqua install' && su - "${USERNAME}" -c 'AQUA_GLOBAL_CONFIG=~/.config/aquaproj-aqua/aqua.yaml aqua install'
# Install AI CLI tools via npm using mise to ensure Node.js is available
RUN mise exec -- npm install -g @just-every/code @qwen-code/qwen-code @google/gemini-cli @openai/codex opencode-ai@latest
# Install the same AI CLI tools for the toolbox user so they are available in the container runtime
RUN su - "${USERNAME}" -c 'mise exec -- npm install -g @just-every/code @qwen-code/qwen-code @google/gemini-cli @openai/codex opencode-ai@latest'
# Prepare workspace directory with appropriate ownership # Prepare workspace directory with appropriate ownership
RUN mkdir -p /workspace \ RUN mkdir -p /workspace \
&& chown "${USER_ID}:${GROUP_ID}" /workspace && chown "${USER_ID}:${GROUP_ID}" /workspace

View File

@@ -10,6 +10,7 @@ Current state:
- Dockerfile installs shell tooling (zsh/bash/fish with Starship & oh-my-zsh), core CLI utilities (curl, wget, git, tmux, screen, htop, btop, entr, httpie, tea, bc, etc.), build-essential + headers, aqua, and mise. Aqua is pinned to specific versions for gh, lazygit, direnv, git-delta, zoxide, just, yq, xh, curlie, chezmoi, shfmt, shellcheck, hadolint, uv, watchexec; direnv/zoxide hooks are enabled for all shells (direnv logging muted). - Dockerfile installs shell tooling (zsh/bash/fish with Starship & oh-my-zsh), core CLI utilities (curl, wget, git, tmux, screen, htop, btop, entr, httpie, tea, bc, etc.), build-essential + headers, aqua, and mise. Aqua is pinned to specific versions for gh, lazygit, direnv, git-delta, zoxide, just, yq, xh, curlie, chezmoi, shfmt, shellcheck, hadolint, uv, watchexec; direnv/zoxide hooks are enabled for all shells (direnv logging muted).
- aqua-managed CLI inventory lives in README.md alongside usage notes; tea installs via direct download with checksum verification (TEA_VERSION build arg). - aqua-managed CLI inventory lives in README.md alongside usage notes; tea installs via direct download with checksum verification (TEA_VERSION build arg).
- mise handles language/tool runtimes; activation wired into zsh, bash, and fish. - mise handles language/tool runtimes; activation wired into zsh, bash, and fish.
- AI CLI tools (just-every/code, QwenLM/qwen-code, google-gemini/gemini-cli, openai/codex, sst/opencode) are installed via npm and available in the PATH.
- docker-compose.yml runs container with host UID/GID, `sleep infinity`, and docker socket mount; run via run.sh/build.sh. Host directories `~/.local/share/mise` and `~/.cache/mise` are mounted for persistent runtimes. - docker-compose.yml runs container with host UID/GID, `sleep infinity`, and docker socket mount; run via run.sh/build.sh. Host directories `~/.local/share/mise` and `~/.cache/mise` are mounted for persistent runtimes.
- Devcontainer config ( .devcontainer/devcontainer.json ) references the compose service. - Devcontainer config ( .devcontainer/devcontainer.json ) references the compose service.
- Documentation: README.md (tooling inventory & workflow) and this PROMPT must stay current, and both should stay aligned with the shared guidance in ../PROMPT. README also notes that build.sh now uses docker buildx with a local cache directory and documents the `dev` → `release-current` → semantic tagging workflow. - Documentation: README.md (tooling inventory & workflow) and this PROMPT must stay current, and both should stay aligned with the shared guidance in ../PROMPT. README also notes that build.sh now uses docker buildx with a local cache directory and documents the `dev` → `release-current` → semantic tagging workflow.

View File

@@ -47,6 +47,7 @@ The compose service mounts the current repo to `/workspace` (read/write) and run
| **Runtime & CLI Managers** | 🪄 `mise` • 💧 `aqua` | `mise` handles language/tool runtimes (activation wired into zsh/bash/fish); `aqua` manages standalone CLIs with config at `~/.config/aquaproj-aqua/aqua.yaml`. | | **Runtime & CLI Managers** | 🪄 `mise` • 💧 `aqua` | `mise` handles language/tool runtimes (activation wired into zsh/bash/fish); `aqua` manages standalone CLIs with config at `~/.config/aquaproj-aqua/aqua.yaml`. |
| **Core CLI Utilities** | 📦 `curl` • 📥 `wget` • 🔐 `ca-certificates` • 🧭 `git` • 🔧 `build-essential` + headers (`pkg-config`, `libssl-dev`, `zlib1g-dev`, `libffi-dev`, `libsqlite3-dev`, `libreadline-dev`, `make`) • 🔍 `ripgrep` • 🧭 `fzf` • 📁 `fd` • 📖 `bat` • 🔗 `openssh-client` • 🧵 `tmux` • 🖥️ `screen` • 📈 `htop` • 📉 `btop` • ♻️ `entr` • 📊 `jq` • 🌐 `httpie` • ☕ `tea` • 🧮 `bc` | Provides ergonomic defaults plus toolchain deps for compiling runtimes (no global language installs). | | **Core CLI Utilities** | 📦 `curl` • 📥 `wget` • 🔐 `ca-certificates` • 🧭 `git` • 🔧 `build-essential` + headers (`pkg-config`, `libssl-dev`, `zlib1g-dev`, `libffi-dev`, `libsqlite3-dev`, `libreadline-dev`, `make`) • 🔍 `ripgrep` • 🧭 `fzf` • 📁 `fd` • 📖 `bat` • 🔗 `openssh-client` • 🧵 `tmux` • 🖥️ `screen` • 📈 `htop` • 📉 `btop` • ♻️ `entr` • 📊 `jq` • 🌐 `httpie` • ☕ `tea` • 🧮 `bc` | Provides ergonomic defaults plus toolchain deps for compiling runtimes (no global language installs). |
| **Aqua-Managed CLIs** | 🐙 `gh` • 🌀 `lazygit` • 🪄 `direnv` • 🎨 `git-delta` • 🧭 `zoxide` • 🧰 `just` • 🧾 `yq` • ⚡ `xh` • 🌍 `curlie` • 🏠 `chezmoi` • 🛠️ `shfmt` • ✅ `shellcheck` • 🐳 `hadolint` • 🐍 `uv` • 🔁 `watchexec` | Extend via `~/.config/aquaproj-aqua/aqua.yaml` and run `aqua install`. Direnv logging is muted and hooks for direnv/zoxide are pre-configured for zsh, bash, and fish. | | **Aqua-Managed CLIs** | 🐙 `gh` • 🌀 `lazygit` • 🪄 `direnv` • 🎨 `git-delta` • 🧭 `zoxide` • 🧰 `just` • 🧾 `yq` • ⚡ `xh` • 🌍 `curlie` • 🏠 `chezmoi` • 🛠️ `shfmt` • ✅ `shellcheck` • 🐳 `hadolint` • 🐍 `uv` • 🔁 `watchexec` | Extend via `~/.config/aquaproj-aqua/aqua.yaml` and run `aqua install`. Direnv logging is muted and hooks for direnv/zoxide are pre-configured for zsh, bash, and fish. |
| **AI CLI Tools** | 🧠 `@just-every/code` • 🤖 `@qwen-code/qwen-code` • 💎 `@google/gemini-cli` • 🔮 `@openai/codex` • 🌐 `opencode-ai` | AI-powered command-line tools for enhanced development workflows. Node.js is installed via mise to support npm package installation. |
| **Container Workflow** | 🐳 Docker socket mount (`/var/run/docker.sock`) | Enables Docker CLIs inside the container; host Docker daemon required. | | **Container Workflow** | 🐳 Docker socket mount (`/var/run/docker.sock`) | Enables Docker CLIs inside the container; host Docker daemon required. |
| **Runtime Environment** | 👤 Non-root user `toolbox` (UID/GID mapped) • 🗂️ `/workspace` mount | Maintains host permissions and isolates artifacts under `artifacts/ToolboxStack/toolbox-base`. | | **Runtime Environment** | 👤 Non-root user `toolbox` (UID/GID mapped) • 🗂️ `/workspace` mount | Maintains host permissions and isolates artifacts under `artifacts/ToolboxStack/toolbox-base`. |