chore: add Cloudron PackageTemplate, helper script; add .gitattributes/.editorconfig; refine .gitignore; improve workspace scripts
This commit is contained in:
25
.editorconfig
Normal file
25
.editorconfig
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[Dockerfile]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.sh]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.yml]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.yaml]
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.json]
|
||||||
|
indent_size = 2
|
||||||
|
|
16
.gitattributes
vendored
Normal file
16
.gitattributes
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
# Enforce LF line endings for key file types
|
||||||
|
*.sh text eol=lf
|
||||||
|
Dockerfile text eol=lf
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
*.md text eol=lf
|
||||||
|
|
||||||
|
# Binary assets
|
||||||
|
*.png binary
|
||||||
|
*.jpg binary
|
||||||
|
*.jpeg binary
|
||||||
|
*.ico binary
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,6 +12,9 @@ packaging_temp/
|
|||||||
*.env
|
*.env
|
||||||
*.local
|
*.local
|
||||||
*.log
|
*.log
|
||||||
|
.envrc
|
||||||
|
.python-version
|
||||||
|
.tool-versions
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
*.swp
|
*.swp
|
||||||
|
11
CloudronPackages/PackageTemplate/.dockerignore
Normal file
11
CloudronPackages/PackageTemplate/.dockerignore
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Ignore typical build context clutter
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
*.log
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
Dockerfile.*
|
||||||
|
.DS_Store
|
||||||
|
|
19
CloudronPackages/PackageTemplate/CloudronManifest.json
Normal file
19
CloudronPackages/PackageTemplate/CloudronManifest.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"manifestVersion": 2,
|
||||||
|
"id": "__APP_ID__",
|
||||||
|
"title": "__APP_TITLE__",
|
||||||
|
"author": "KNEL",
|
||||||
|
"description": "Cloudron packaging template for __APP_TITLE__",
|
||||||
|
"website": "https://example.com",
|
||||||
|
"contactEmail": "admin@example.com",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"changelog": "Initial package template",
|
||||||
|
"healthCheckPath": "/",
|
||||||
|
"httpPort": __HTTP_PORT__,
|
||||||
|
"addons": {
|
||||||
|
"localstorage": {}
|
||||||
|
},
|
||||||
|
"tags": ["template", "example"],
|
||||||
|
"icon": "logo.png"
|
||||||
|
}
|
||||||
|
|
39
CloudronPackages/PackageTemplate/Dockerfile
Normal file
39
CloudronPackages/PackageTemplate/Dockerfile
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
FROM cloudron/base:4.2.0
|
||||||
|
|
||||||
|
# Metadata labels (edit as needed)
|
||||||
|
LABEL org.opencontainers.image.title="__APP_TITLE__"
|
||||||
|
LABEL org.opencontainers.image.description="Cloudron package for __APP_TITLE__"
|
||||||
|
LABEL org.opencontainers.image.source="https://example.com"
|
||||||
|
|
||||||
|
# Install OS dependencies here as needed
|
||||||
|
# RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
# curl ca-certificates tini \
|
||||||
|
# && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# App code lives in /app/code (read-only at runtime)
|
||||||
|
WORKDIR /app/code
|
||||||
|
|
||||||
|
# Copy application code (adjust as needed)
|
||||||
|
# COPY . /app/code
|
||||||
|
|
||||||
|
# Create persistent directory for application data
|
||||||
|
RUN mkdir -p /app/data && chown -R cloudron:cloudron /app/data
|
||||||
|
|
||||||
|
# Copy startup script
|
||||||
|
COPY start.sh /app/pkg/start.sh
|
||||||
|
RUN chmod +x /app/pkg/start.sh && chown cloudron:cloudron /app/pkg/start.sh
|
||||||
|
|
||||||
|
USER cloudron
|
||||||
|
|
||||||
|
# Expose the app port specified in manifest
|
||||||
|
EXPOSE __HTTP_PORT__
|
||||||
|
|
||||||
|
# Default environment (customize per app)
|
||||||
|
ENV NODE_ENV=production \
|
||||||
|
APP_PORT=__HTTP_PORT__
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
||||||
|
CMD curl -fsS http://127.0.0.1:${APP_PORT}/ || exit 1
|
||||||
|
|
||||||
|
CMD ["/app/pkg/start.sh"]
|
||||||
|
|
24
CloudronPackages/PackageTemplate/README.md
Normal file
24
CloudronPackages/PackageTemplate/README.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Package Template for Cloudron Apps
|
||||||
|
|
||||||
|
This is a minimal template to package an application for Cloudron.
|
||||||
|
|
||||||
|
Replace placeholders in files with your app specifics:
|
||||||
|
- `__APP_ID__` (e.g., com.example.myapp)
|
||||||
|
- `__APP_TITLE__` (human name)
|
||||||
|
- `__HTTP_PORT__` (default internal app port)
|
||||||
|
|
||||||
|
Files
|
||||||
|
- `CloudronManifest.json` – base manifest
|
||||||
|
- `Dockerfile` – uses cloudron/base, non-root user, healthcheck
|
||||||
|
- `start.sh` – startup script with addon detection examples
|
||||||
|
- `nginx.conf` (optional) – example reverse proxy
|
||||||
|
- `supervisord.conf` (optional) – process manager example
|
||||||
|
- `config.yaml` (optional) – sample app config
|
||||||
|
- `logo.png` – add your 512x512 PNG icon here (not provided in template)
|
||||||
|
|
||||||
|
Usage
|
||||||
|
1. Create a new package from this template using `scripts/new-package.sh`:
|
||||||
|
`scripts/new-package.sh MyApp --id com.example.myapp --title "My App" --port 3000`
|
||||||
|
2. Adjust Dockerfile and start.sh to run your app.
|
||||||
|
3. Build and test locally; then commit and push.
|
||||||
|
|
11
CloudronPackages/PackageTemplate/config.yaml
Normal file
11
CloudronPackages/PackageTemplate/config.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Example configuration template for __APP_TITLE__
|
||||||
|
server:
|
||||||
|
port: __HTTP_PORT__
|
||||||
|
|
||||||
|
data:
|
||||||
|
dir: /app/data
|
||||||
|
|
||||||
|
database:
|
||||||
|
# url: ${CLOUDRON_POSTGRESQL_URL}
|
||||||
|
# redis: ${CLOUDRON_REDIS_URL}
|
||||||
|
|
26
CloudronPackages/PackageTemplate/nginx.conf
Normal file
26
CloudronPackages/PackageTemplate/nginx.conf
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
user cloudron;
|
||||||
|
worker_processes auto;
|
||||||
|
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
pid /run/nginx.pid;
|
||||||
|
|
||||||
|
events { worker_connections 1024; }
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
sendfile on;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen __HTTP_PORT__;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_pass http://127.0.0.1:__HTTP_PORT__;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
39
CloudronPackages/PackageTemplate/start.sh
Normal file
39
CloudronPackages/PackageTemplate/start.sh
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
log() { echo "[start] $(date -Is) $*"; }
|
||||||
|
abort() { echo "[start] ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
: "${APP_PORT:=__HTTP_PORT__}"
|
||||||
|
|
||||||
|
log "Starting __APP_TITLE__ on port ${APP_PORT}"
|
||||||
|
|
||||||
|
# Example: ensure /app/data exists and is writable
|
||||||
|
mkdir -p /app/data
|
||||||
|
chown -R cloudron:cloudron /app/data || true
|
||||||
|
|
||||||
|
# Example addon integration (uncomment and adapt as needed)
|
||||||
|
# if [[ -n "${CLOUDRON_POSTGRESQL_URL:-}" ]]; then
|
||||||
|
# log "Detected PostgreSQL addon"
|
||||||
|
# # Use $CLOUDRON_POSTGRESQL_* env vars
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# if [[ -n "${CLOUDRON_REDIS_URL:-}" ]]; then
|
||||||
|
# log "Detected Redis addon"
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# If your app needs config generation, do it here
|
||||||
|
# cat > /app/data/config.yaml <<'YAML'
|
||||||
|
# key: value
|
||||||
|
# YAML
|
||||||
|
|
||||||
|
# Example: start a simple HTTP server (placeholder)
|
||||||
|
# Replace with your actual app start command
|
||||||
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
|
log "Launching placeholder server: python3 -m http.server ${APP_PORT}"
|
||||||
|
exec python3 -m http.server "${APP_PORT}" --bind 0.0.0.0
|
||||||
|
else
|
||||||
|
abort "No application command configured. Replace placeholder with your app's start command."
|
||||||
|
fi
|
||||||
|
|
12
CloudronPackages/PackageTemplate/supervisord.conf
Normal file
12
CloudronPackages/PackageTemplate/supervisord.conf
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[supervisord]
|
||||||
|
logfile=/var/log/supervisor/supervisord.log
|
||||||
|
pidfile=/run/supervisord.pid
|
||||||
|
nodaemon=true
|
||||||
|
|
||||||
|
[program:app]
|
||||||
|
command=/app/pkg/start.sh
|
||||||
|
autorestart=true
|
||||||
|
stdout_logfile=/var/log/supervisor/app.stdout.log
|
||||||
|
stderr_logfile=/var/log/supervisor/app.stderr.log
|
||||||
|
user=cloudron
|
||||||
|
|
@@ -205,10 +205,25 @@ https://github.com/funmusicplace/mirlo.git
|
|||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
cd Docker
|
WORKDIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
TARGET_DIR="${WORKDIR}/Docker"
|
||||||
|
mkdir -p "$TARGET_DIR"
|
||||||
|
|
||||||
IFS=$'\n\t'
|
# If REPOS.txt exists, read additional repos (lines; ignore # and blanks)
|
||||||
|
EXTRA_REPOS_FILE="${WORKDIR}/REPOS.txt"
|
||||||
|
if [[ -f "$EXTRA_REPOS_FILE" ]]; then
|
||||||
|
mapfile -t EXTRA_REPOS < <(sed -e 's/#.*$//' -e '/^\s*$/d' "$EXTRA_REPOS_FILE")
|
||||||
|
else
|
||||||
|
EXTRA_REPOS=()
|
||||||
|
fi
|
||||||
|
|
||||||
for GIT_REPO in ${GIT_REPO_LIST[@]};do
|
ALL_REPOS=("${GIT_REPO_LIST[@]}" "${EXTRA_REPOS[@]}")
|
||||||
git clone --depth 1 $GIT_REPO || true
|
|
||||||
done
|
echo "Cloning to: $TARGET_DIR"
|
||||||
|
printf ' - %s\n' "${ALL_REPOS[@]}"
|
||||||
|
|
||||||
|
cd "$TARGET_DIR"
|
||||||
|
|
||||||
|
# Parallel clones (default 4 jobs). Avoid failing the whole script on single failures.
|
||||||
|
JOBS="${JOBS:-4}"
|
||||||
|
printf '%s\n' "${ALL_REPOS[@]}" | xargs -n1 -P "$JOBS" -I{} bash -lc 'repo="{}"; name=$(basename -s .git "$repo"); if [[ -d "$name/.git" ]]; then echo "exists: $name"; else git clone --depth 1 "$repo" "$name" || echo "failed: $repo"; fi'
|
||||||
|
@@ -27,18 +27,21 @@ set -o nounset
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
set -o functrace
|
set -o functrace
|
||||||
|
|
||||||
|
WORKDIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
TARGET_DIR="${WORKDIR}/Docker"
|
||||||
|
|
||||||
cd Docker
|
cd "$TARGET_DIR"
|
||||||
|
|
||||||
GIT_REPO_LIST="$(ls -d */)"
|
# Iterate only over directories that are git repos
|
||||||
|
while IFS= read -r -d '' repo_dir; do
|
||||||
IFS=$'\n\t'
|
echo "Updating: ${repo_dir}"
|
||||||
|
pushd "$repo_dir" >/dev/null
|
||||||
for GIT_REPO in ${GIT_REPO_LIST[@]};
|
if [[ -d .git ]]; then
|
||||||
do
|
git -c advice.detachedHead=false fetch --all --prune || true
|
||||||
CURRENT_DIR=$(realpath $PWD)
|
# Fast-forward only to avoid unintended merges
|
||||||
echo "Updating from $GIT_REPO..."
|
git -c advice.detachedHead=false pull --ff-only || true
|
||||||
cd $GIT_REPO
|
else
|
||||||
git pull
|
echo "Skipping (not a git repo): ${repo_dir}"
|
||||||
cd $CURRENT_DIR
|
fi
|
||||||
done
|
popd >/dev/null
|
||||||
|
done < <(find . -mindepth 1 -maxdepth 1 -type d -print0)
|
||||||
|
53
scripts/new-package.sh
Executable file
53
scripts/new-package.sh
Executable file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: scripts/new-package.sh <AppName> [--id <com.example.app>] [--title <Title>] [--port <port>]
|
||||||
|
|
||||||
|
Creates CloudronPackages/<AppName> from PackageTemplate and replaces placeholders:
|
||||||
|
__APP_ID__, __APP_TITLE__, __HTTP_PORT__
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
scripts/new-package.sh MyApp --id com.example.myapp --title "My App" --port 3000
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $# -lt 1 ]]; then
|
||||||
|
usage; exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
APP_NAME="$1"; shift
|
||||||
|
APP_ID="com.example.${APP_NAME,,}"
|
||||||
|
APP_TITLE="$APP_NAME"
|
||||||
|
HTTP_PORT="3000"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--id) APP_ID="$2"; shift 2;;
|
||||||
|
--title) APP_TITLE="$2"; shift 2;;
|
||||||
|
--port) HTTP_PORT="$2"; shift 2;;
|
||||||
|
-h|--help) usage; exit 0;;
|
||||||
|
*) echo "Unknown argument: $1"; usage; exit 1;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
SRC_DIR="CloudronPackages/PackageTemplate"
|
||||||
|
DEST_DIR="CloudronPackages/${APP_NAME}"
|
||||||
|
|
||||||
|
[[ -d "$SRC_DIR" ]] || { echo "Template not found: $SRC_DIR"; exit 1; }
|
||||||
|
[[ -e "$DEST_DIR" ]] && { echo "Destination already exists: $DEST_DIR"; exit 1; }
|
||||||
|
|
||||||
|
mkdir -p "$DEST_DIR"
|
||||||
|
cp -a "$SRC_DIR"/. "$DEST_DIR"/
|
||||||
|
|
||||||
|
# Replace placeholders in text files
|
||||||
|
find "$DEST_DIR" -type f \( -name "*" ! -name "*.png" \) -print0 | while IFS= read -r -d '' f; do
|
||||||
|
sed -i "s#__APP_ID__#${APP_ID}#g" "$f"
|
||||||
|
sed -i "s#__APP_TITLE__#${APP_TITLE}#g" "$f"
|
||||||
|
sed -i "s#__HTTP_PORT__#${HTTP_PORT}#g" "$f"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Created package at: $DEST_DIR"
|
||||||
|
echo "Next steps: edit Dockerfile and start.sh to run your app. Add logo.png if desired."
|
||||||
|
|
Reference in New Issue
Block a user