diff --git a/docs/APP-ct.md b/docs/APP-ct.md index aa6793f29..e5dfa42e5 100644 --- a/docs/APP-ct.md +++ b/docs/APP-ct.md @@ -1,273 +1,493 @@ -# **AppName.sh Scripts** +# πŸš€ **Application Container Scripts (ct/AppName.sh)** - `AppName.sh` scripts found in the `/ct` directory. These scripts are responsible for the installation of the desired application. For this guide we take `/ct/snipeit.sh` as example. +**Modern Guide to Creating LXC Container Installation Scripts** -## Table of Contents +> **Updated**: December 2025 +> **Context**: Fully integrated with build.func, advanced_settings wizard, and defaults system +> **Example Used**: `/ct/pihole.sh`, `/ct/docker.sh` -- [**AppName.sh Scripts**](#appnamesh-scripts) - - [Table of Contents](#table-of-contents) - - [1. **File Header**](#1-file-header) - - [1.1 **Shebang**](#11-shebang) - - [1.2 **Import Functions**](#12-import-functions) - - [1.3 **Metadata**](#13-metadata) - - [2 **Variables and function import**](#2-variables-and-function-import) - - [2.1 **Default Values**](#21-default-values) - - [2.2 **πŸ“‹ App output \& base settings**](#22--app-output--base-settings) - - [2.3 **πŸ›  Core functions**](#23--core-functions) - - [3 **Update function**](#3-update-function) - - [3.1 **Function Header**](#31-function-header) - - [3.2 **Check APP**](#32-check-app) - - [3.3 **Check version**](#33-check-version) - - [3.4 **Verbosity**](#34-verbosity) - - [3.5 **Backups**](#35-backups) - - [3.6 **Cleanup**](#36-cleanup) - - [3.7 **No update function**](#37-no-update-function) - - [4 **End of the script**](#4-end-of-the-script) - - [5. **Contribution checklist**](#5-contribution-checklist) +--- -## 1. **File Header** +## πŸ“‹ Table of Contents -### 1.1 **Shebang** +- [Overview](#overview) +- [Architecture & Flow](#architecture--flow) +- [File Structure](#file-structure) +- [Complete Script Template](#complete-script-template) +- [Function Reference](#function-reference) +- [Advanced Features](#advanced-features) +- [Real Examples](#real-examples) +- [Troubleshooting](#troubleshooting) +- [Contribution Checklist](#contribution-checklist) -- Use `#!/usr/bin/env bash` as the shebang. +--- + +## Overview + +### Purpose + +Container scripts (`ct/AppName.sh`) are **entry points for creating LXC containers** with specific applications pre-installed. They: + +1. Define container defaults (CPU, RAM, disk, OS) +2. Call the main build orchestrator (`build.func`) +3. Implement application-specific update mechanisms +4. Provide user-facing success messages + +### Execution Context + +``` +Proxmox Host + ↓ +ct/AppName.sh sourced (runs as root on host) + ↓ +build.func: Creates LXC container + runs install script inside + ↓ +install/AppName-install.sh (runs inside container) + ↓ +Container ready with app installed +``` + +### Key Integration Points + +- **build.func** - Main orchestrator (container creation, storage, variable management) +- **install.func** - Container-specific setup (OS update, package management) +- **tools.func** - Tool installation helpers (repositories, GitHub releases) +- **core.func** - UI/messaging functions (colors, spinners, validation) +- **error_handler.func** - Error handling and signal management + +--- + +## Architecture & Flow + +### Container Creation Flow + +``` +START: bash ct/pihole.sh + ↓ +[1] Set APP, var_*, defaults + ↓ +[2] header_info() β†’ Display ASCII art + ↓ +[3] variables() β†’ Parse arguments & load build.func + ↓ +[4] color() β†’ Setup ANSI codes + ↓ +[5] catch_errors() β†’ Setup trap handlers + ↓ +[6] install_script() β†’ Show mode menu (5 options) + ↓ + β”œβ”€ INSTALL_MODE="0" (Default) + β”œβ”€ INSTALL_MODE="1" (Advanced - 19-step wizard) + β”œβ”€ INSTALL_MODE="2" (User Defaults) + β”œβ”€ INSTALL_MODE="3" (App Defaults) + └─ INSTALL_MODE="4" (Settings Menu) + ↓ +[7] advanced_settings() β†’ Collect user configuration (if mode=1) + ↓ +[8] start() β†’ Confirm or re-edit settings + ↓ +[9] build_container() β†’ Create LXC + execute install script + ↓ +[10] description() β†’ Set container description + ↓ +[11] SUCCESS β†’ Display access URL + ↓ +END +``` + +### Default Values Precedence + +``` +Priority 1 (Highest): Environment Variables (var_cpu, var_ram, etc.) +Priority 2: App-Specific Defaults (/defaults/AppName.vars) +Priority 3: User Global Defaults (/default.vars) +Priority 4 (Lowest): Built-in Defaults (in build.func) +``` + +--- + +## File Structure + +### Minimal ct/AppName.sh Template + +``` +#!/usr/bin/env bash # [1] Shebang + # [2] Copyright/License +source <(curl -s .../misc/build.func) # [3] Import functions + # [4] APP metadata +APP="AppName" # [5] Default values +var_tags="tag1;tag2" +var_cpu="2" +var_ram="2048" +... + +header_info "$APP" # [6] Display header +variables # [7] Process arguments +color # [8] Setup colors +catch_errors # [9] Setup error handling + +function update_script() { ... } # [10] Update function (optional) + +start # [11] Launch container creation +build_container +description +msg_ok "Completed Successfully!\n" +``` + +--- + +## Complete Script Template + +### 1. File Header & Imports ```bash #!/usr/bin/env bash -``` - -### 1.2 **Import Functions** - -- Import the build.func file. -- When developing your own script, change the URL to your own repository. - -> [!IMPORTANT] -> You also need to change all apperances of this URL in `misc/build.func` and `misc/install.func` - - -Example for development: - -```bash -source <(curl -s https://raw.githubusercontent.com/[USER]/[REPO]/refs/heads/[BRANCH]/misc/build.func) -``` - -Final script: - -```bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) -``` - -> [!CAUTION] -> Before opening a Pull Request, change the URL to point to the community-scripts repo. - -### 1.3 **Metadata** - -- Add clear comments for script metadata, including author, copyright, and license information. - -Example: - -```bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: [YourUserName] -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: [SOURCE_URL] +# Author: YourUsername +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/example/project + +# Import main orchestrator +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) ``` -> [!NOTE]: -> -> - Add your username and source URL -> - For existing scripts, add "| Co-Author [YourUserName]" after the current author +> **⚠️ IMPORTANT**: Before opening a PR, change URL to `community-scripts` repo! ---- - -## 2 **Variables and function import** -> -> [!NOTE] -> You need to have all this set in your script, otherwise it will not work! - -### 2.1 **Default Values** - -- This section sets the default values for the container. -- `APP` needs to be set to the application name and must be equal to the filenames of your scripts. -- `var_tags`: You can set Tags for the CT wich show up in the Proxmox UI. DonΒ΄t overdo it! - ->[!NOTE] ->Description for all Default Values -> ->| Variable | Description | Notes | ->|----------|-------------|-------| ->| `APP` | Application name | Must match ct\AppName.sh | ->| `TAGS` | Proxmox display tags without Spaces, only ; | Limit the number | ->| `var_cpu` | CPU cores | Number of cores | ->| `var_ram` | RAM | In MB | ->| `var_disk` | Disk capacity | In GB | ->| `var_os` | Operating system | alpine, debian, ubuntu | ->| `var_version` | OS version | e.g., 3.20, 11, 12, 20.04 | ->| `var_unprivileged` | Container type | 1 = Unprivileged, 0 = Privileged | - -Example: +### 2. Application Metadata ```bash -APP="SnipeIT" -var_tags="asset-management;foss" -var_cpu="2" -var_ram="2048" -var_disk="4" -var_os="debian" -var_version="12" -var_unprivileged="1" +# Application Configuration +APP="ApplicationName" +var_tags="tag1;tag2;tag3" # Max 3-4 tags, no spaces, semicolon-separated + +# Container Resources +var_cpu="2" # CPU cores +var_ram="2048" # RAM in MB +var_disk="10" # Disk in GB + +# Container Type & OS +var_os="debian" # Options: alpine, debian, ubuntu +var_version="12" # Alpine: 3.20+, Debian: 11-13, Ubuntu: 20.04+ +var_unprivileged="1" # 1=unprivileged (secure), 0=privileged (rarely needed) ``` -## 2.2 **πŸ“‹ App output & base settings** +**Variable Naming Convention**: +- Variables exposed to user: `var_*` (e.g., `var_cpu`, `var_hostname`, `var_ssh`) +- Internal variables: lowercase (e.g., `container_id`, `app_version`) + +### 3. Display & Initialization ```bash -# App Output & Base Settings +# Display header ASCII art header_info "$APP" -base_settings -``` -- `header_info`: Generates ASCII header for APP -- `base_settings`: Allows overwriting variable values - -## 2.3 **πŸ›  Core functions** - -```bash -# Core +# Process command-line arguments and load configuration variables + +# Setup ANSI color codes and formatting color + +# Initialize error handling (trap ERR, EXIT, INT, TERM) catch_errors ``` -- `variables`: Processes input and prepares variables -- `color`: Sets icons, colors, and formatting -- `catch_errors`: Enables error handling - ---- - -## 3 **Update function** - -### 3.1 **Function Header** - -- If applicable write a function that updates the application and the OS in the container. -- Each update function starts with the same code: +### 4. Update Function (Highly Recommended) ```bash function update_script() { header_info + + # Always start with these checks check_container_storage check_container_resources -``` -### 3.2 **Check APP** - -- Before doing anything update-wise, check if the app is installed in the container. - -Example: - -```bash -if [[ ! -d /opt/snipe-it ]]; then + # Verify app is installed + if [[ ! -d /opt/appname ]]; then msg_error "No ${APP} Installation Found!" exit fi -``` -### 3.3 **Check version** + # Get latest version from GitHub + RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \ + grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') -- Before updating, check if a new version exists. - - We use the `${APPLICATION}_version.txt` file created in `/opt` during the install to compare new versions against the currently installed version. - -Example with a Github Release: - -```bash - RELEASE=$(curl -fsSL https://api.github.com/repos/snipe/snipe-it/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + # Compare with saved version if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then msg_info "Updating ${APP} to v${RELEASE}" - #DO UPDATE + + # Backup user data + cp -r /opt/appname /opt/appname-backup + + # Perform update + cd /opt + wget -q "https://github.com/user/repo/releases/download/v${RELEASE}/app-${RELEASE}.tar.gz" + tar -xzf app-${RELEASE}.tar.gz + + # Restore user data + cp /opt/appname-backup/config/* /opt/appname/config/ + + # Cleanup + rm -rf app-${RELEASE}.tar.gz /opt/appname-backup + + # Save new version + echo "${RELEASE}" > /opt/${APP}_version.txt + + msg_ok "Updated ${APP} to v${RELEASE}" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}." + fi + + exit +} +``` + +### 5. Script Launch + +```bash +# Start the container creation workflow +start + +# Build the container with selected configuration +build_container + +# Set container description/notes in Proxmox UI +description + +# Display success message +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" +``` + +--- + +## Function Reference + +### Core Functions (From build.func) + +#### `variables()` + +**Purpose**: Initialize container variables, load user arguments, setup orchestration + +**Triggered by**: Called automatically at script start + +**Behavior**: +1. Parse command-line arguments (if any) +2. Generate random UUID for session tracking +3. Load container storage from Proxmox +4. Initialize application-specific defaults +5. Setup SSH/environment configuration + +**Example Output**: +``` +Setting up variables... +Container ID: 100 +Storage: local-lvm +Install method: Default +``` + +#### `start()` + +**Purpose**: Launch the container creation menu with 5 installation modes + +**Triggered by**: Called just before `build_container()` + +**Menu Options**: +``` +1. Default Installation (Quick setup, predefined settings) +2. Advanced Installation (19-step wizard with full control) +3. User Defaults (Load ~/.community-scripts/default.vars) +4. App Defaults (Load /defaults/AppName.vars) +5. Settings Menu (Interactive mode selection) +``` + +**User Flow**: +``` +Select installation mode: +1) Default +2) Advanced +3) User Defaults +4) App Defaults +5) Settings Menu +Enter choice: 2 +``` + +#### `build_container()` + +**Purpose**: Main orchestrator for LXC container creation + +**Operations**: +1. Validates all variables +2. Creates LXC container via `pct create` +3. Executes `install/AppName-install.sh` inside container +4. Monitors installation progress +5. Handles errors and rollback on failure + +**Exit Codes**: +- `0` - Success +- `1-255` - Various error conditions (see error_handler.func) + +#### `description()` + +**Purpose**: Set container description/notes visible in Proxmox UI + +**Format**: +``` +AppName +IP: [IP] +Version: [Version] +Tags: [Tags] +``` + +#### `header_info()` + +**Purpose**: Display ASCII art header for application + +**Sources**: +- Tries `/usr/local/community-scripts/headers/ct/appname` (cached) +- Falls back to remote fetch from GitHub +- Returns silently if not found + +--- + +## Advanced Features + +### 1. Integration with Defaults System + +#### Save App Defaults After Installation + +```bash +# At end of install script, after successful setup: +maybe_offer_save_app_defaults + +# Output: +# "Save these settings as App Defaults for AppName? (Y/n)" +# Yes β†’ Saves to /defaults/appname.vars +# No β†’ Skips saving +``` + +#### Load Saved Defaults During Container Creation + +```bash +# In ct/AppName.sh, user selects "App Defaults" mode +# Automatically loads /defaults/appname.vars +# Container uses previously saved configuration +``` + +### 2. Custom Configuration Menus + +If your app has additional setup beyond standard vars: + +```bash +# In ct/AppName.sh, after variables() +custom_app_settings() { + CONFIGURE_DB=$(whiptail --title "Database Setup" \ + --yesno "Would you like to configure a custom database?" 8 60) + + if [[ $? -eq 0 ]]; then + DB_HOST=$(whiptail --inputbox "Database Host:" 8 60 3>&1 1>&2 2>&3) + DB_PORT=$(whiptail --inputbox "Database Port:" 8 60 "3306" 3>&1 1>&2 2>&3) + fi +} + +custom_app_settings +``` + +### 3. Version Tracking + +Save installed version for update checks: + +```bash +# In install script, after successful app download: +RELEASE="1.2.3" +echo "${RELEASE}" > /opt/${APP}_version.txt + +# In update function, compare: +CURRENT=$(cat /opt/${APP}_version.txt 2>/dev/null) +LATEST=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | jq -r '.tag_name') + +if [[ "$LATEST" != "$CURRENT" ]]; then + echo "Update available: $CURRENT β†’ $LATEST" +fi +``` + +### 4. Health Check Functions + +Add custom validation: + +```bash +function health_check() { + header_info + + if [[ ! -d /opt/appname ]]; then + msg_error "Application not found!" + exit 1 + fi + + if ! systemctl is-active --quiet appname; then + msg_error "Application service not running" + exit 1 + fi + + msg_ok "Health check passed" +} + +# Called via: bash ct/appname.sh health_check +``` + +--- + +## Real Examples + +### Example 1: Simple Web App (Debian-based) + +```bash +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) + +APP="Homarr" +var_tags="dashboard;homepage" +var_cpu="2" +var_ram="1024" +var_disk="5" +var_os="debian" +var_version="12" +var_unprivileged="1" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/homarr ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/ajnart/homarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') + + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Updating ${APP} to v${RELEASE}" + systemctl stop homarr + + cd /opt/homarr + wget -q "https://github.com/ajnart/homarr/releases/download/v${RELEASE}/docker-compose.yml" + docker-compose up -d + + echo "${RELEASE}" > /opt/${APP}_version.txt + msg_ok "Updated ${APP} to v${RELEASE}" else msg_ok "No update required. ${APP} is already at v${RELEASE}." fi exit } -``` -### 3.4 **Verbosity** - -- Use the appropriate flag (**-q** in the examples) for a command to suppress its output. -Example: - -```bash -wget -q -unzip -q -``` - -- If a command does not come with this functionality use `&>/dev/null` to suppress it's output. - -Example: - -```bash -php artisan migrate --force &>/dev/null -php artisan config:clear &>/dev/null -``` - -### 3.5 **Backups** - -- Backup user data if necessary. -- Move all user data back in the directory when the update is finished. - ->[!NOTE] ->This is not meant to be a permanent backup - -Example backup: - -```bash - mv /opt/snipe-it /opt/snipe-it-backup -``` - -Example config restore: - -```bash - cp /opt/snipe-it-backup/.env /opt/snipe-it/.env - cp -r /opt/snipe-it-backup/public/uploads/ /opt/snipe-it/public/uploads/ - cp -r /opt/snipe-it-backup/storage/private_uploads /opt/snipe-it/storage/private_uploads -``` - -### 3.6 **Cleanup** - -- Do not forget to remove any temporary files/folders such as zip-files or temporary backups. -Example: - -```bash - rm -rf /opt/v${RELEASE}.zip - rm -rf /opt/snipe-it-backup -``` - -### 3.7 **No update function** - -- In case you can not provide a update function use the following code to provide user feedback. - -```bash -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/snipeit ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_error "There is currently no automatic update function for ${APP}." - exit -} -``` - ---- - -## 4 **End of the script** - -- `start`: Launches Whiptail dialogue -- `build_container`: Collects and integrates user settings -- `description`: Sets LXC container description -- With `echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}"` you can point the user to the IP:PORT/folder needed to access the app. - -```bash start build_container description @@ -275,18 +495,223 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5100${CL}" +``` + +### Example 2: Database App (Alpine-based) + +```bash +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) + +APP="PostgreSQL" +var_tags="database;sql" +var_cpu="4" +var_ram="4096" +var_disk="20" +var_os="alpine" +var_version="3.20" +var_unprivileged="1" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + + if ! command -v psql &>/dev/null; then + msg_error "PostgreSQL not installed!" + exit + fi + + msg_info "Updating Alpine packages" + apk update + apk upgrade + msg_ok "Updated Alpine packages" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Connect using:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}psql -h ${IP} -U postgres${CL}" ``` --- -## 5. **Contribution checklist** +## Troubleshooting -- [ ] Shebang is correctly set (`#!/usr/bin/env bash`). -- [ ] Correct link to *build.func* -- [ ] Metadata (author, license) is included at the top. -- [ ] Variables follow naming conventions. -- [ ] Update function exists. -- [ ] Update functions checks if app is installed an for new version. -- [ ] Update function up temporary files. -- [ ] Script ends with a helpful message for the user to reach the application. +### Container Creation Fails + +**Symptom**: `pct create` exits with error code 209 + +**Causes**: +1. CTID already exists: `pct list` shows duplicate +2. Storage full: Check storage space +3. Network template unavailable + +**Solution**: +```bash +# Check existing containers +pct list | grep CTID + +# Remove conflicting container +pct destroy CTID + +# Retry ct/AppName.sh +``` + +### Update Function Doesn't Detect New Version + +**Symptom**: Update available but script says "already at latest" + +**Causes**: +1. Version file missing: `/opt/AppName_version.txt` +2. GitHub API rate limit exceeded +3. Release tag format mismatch + +**Debug**: +```bash +# Check version file +cat /opt/AppName_version.txt + +# Test GitHub API +curl -fsSL https://api.github.com/repos/user/repo/releases/latest | grep tag_name + +# Inside container +bash ct/appname.sh update_script +``` + +### Header ASCII Art Not Displaying + +**Symptom**: Container script runs but no header shown + +**Causes**: +1. Header file not in repository +2. Caching issue + +**Solution**: +```bash +# Create header file manually +mkdir -p /usr/local/community-scripts/headers/ct +echo "Your ASCII art here" > /usr/local/community-scripts/headers/ct/appname + +# Or remove cache to force re-download +rm -f /usr/local/community-scripts/headers/ct/appname +``` + +--- + +## Contribution Checklist + +Before submitting a PR: + +### Script Structure +- [ ] Shebang is `#!/usr/bin/env bash` +- [ ] Imports `build.func` from community-scripts repo (not personal fork) +- [ ] Copyright header with author and source URL +- [ ] APP variable matches filename +- [ ] `var_tags` are semicolon-separated (no spaces) + +### Default Values +- [ ] `var_cpu` set appropriately (2-4 for most apps) +- [ ] `var_ram` set appropriately (1024-4096 MB minimum) +- [ ] `var_disk` sufficient for app + data (5-20 GB) +- [ ] `var_os` is realistic (Alpine if lightweight, Debian/Ubuntu otherwise) +- [ ] `var_unprivileged="1"` unless app absolutely needs privileges + +### Functions +- [ ] `update_script()` implemented (or marked as unavailable) +- [ ] Update function checks if app installed +- [ ] Update function checks for new version +- [ ] Update function performs cleanup after update +- [ ] Proper error handling with `msg_error` on failure + +### Output +- [ ] Success message displayed with access URL +- [ ] URL format: `http://IP:PORT/path` (if web-based) +- [ ] Uses `msg_ok`, `msg_info`, `msg_error` for feedback + +### Testing +- [ ] Script tested with default installation +- [ ] Script tested with advanced (19-step) installation +- [ ] Update function tested on existing installation +- [ ] Error handling tested (invalid settings, network issues) + +--- + +## Best Practices + +### βœ… DO: + +1. **Use meaningful defaults** + ```bash + var_cpu="2" # βœ… Good: Typical workload + var_cpu="128" # ❌ Bad: Unrealistic + ``` + +2. **Implement version tracking** + ```bash + echo "${RELEASE}" > /opt/${APP}_version.txt # βœ… Good + # ❌ Bad: No version tracking + ``` + +3. **Handle edge cases** + ```bash + if [[ ! -f /opt/${APP}_version.txt ]]; then + msg_info "First installation detected" + fi + ``` + +4. **Use proper messaging** + ```bash + msg_info "Updating..." # βœ… Good: Clear status + echo "Updating..." # ❌ Bad: No formatting + ``` + +### ❌ DON'T: + +1. **Hardcode versions** + ```bash + RELEASE="1.2.3" # ❌ Bad: Won't auto-update + ``` + +2. **Use custom color codes** + ```bash + echo -e "\033[32mSuccess" # ❌ Bad: Use $GN instead + ``` + +3. **Forget error handling** + ```bash + wget file.zip # ❌ Bad: No error check + if ! wget -q file.zip; then # βœ… Good + msg_error "Download failed" + fi + ``` + +4. **Leave temporary files** + ```bash + rm -rf /opt/file.zip # βœ… Always cleanup + ``` + +--- + +## Related Documentation + +- [install/AppName-install.sh Guide](UPDATED_APP-install.md) +- [build.func Wiki](../misc/build.func.md) +- [tools.func Wiki](../misc/tools.func.md) +- [Defaults System Guide](../DEFAULTS_SYSTEM_GUIDE.md) + +--- + +**Last Updated**: December 2025 +**Compatibility**: ProxmoxVED with build.func v3+ +**Questions?** Open an issue in the repository diff --git a/docs/APP-install.md b/docs/APP-install.md index 8a9c7e65a..a754e3316 100644 --- a/docs/APP-install.md +++ b/docs/APP-install.md @@ -1,78 +1,128 @@ +# πŸ› οΈ **Application Installation Scripts (install/AppName-install.sh)** -# **AppName-install.sh Scripts** +**Modern Guide to Writing In-Container Installation Scripts** - `AppName-install.sh` scripts found in the `/install` directory. These scripts are responsible for the installation of the application. For this guide we take `/install/snipeit-install.sh` as example. +> **Updated**: December 2025 +> **Context**: Integrated with tools.func, error_handler.func, and install.func +> **Examples Used**: `/install/pihole-install.sh`, `/install/mealie-install.sh` -## Table of Contents +--- -- [**AppName-install.sh Scripts**](#appname-installsh-scripts) - - [Table of Contents](#table-of-contents) - - [1. **File header**](#1-file-header) - - [1.1 **Shebang**](#11-shebang) - - [1.2 **Comments**](#12-comments) - - [1.3 **Variables and function import**](#13-variables-and-function-import) - - [2. **Variable naming and management**](#2-variable-naming-and-management) - - [2.1 **Naming conventions**](#21-naming-conventions) - - [3. **Dependencies**](#3-dependencies) - - [3.1 **Install all at once**](#31-install-all-at-once) - - [3.2 **Collapse dependencies**](#32-collapse-dependencies) - - [4. **Paths to application files**](#4-paths-to-application-files) - - [5. **Version management**](#5-version-management) - - [5.1 **Install the latest release**](#51-install-the-latest-release) - - [5.2 **Save the version for update checks**](#52-save-the-version-for-update-checks) - - [6. **Input and output management**](#6-input-and-output-management) - - [6.1 **User feedback**](#61-user-feedback) - - [6.2 **Verbosity**](#62-verbosity) - - [7. **String/File Manipulation**](#7-stringfile-manipulation) - - [7.1 **File Manipulation**](#71-file-manipulation) - - [8. **Security practices**](#8-security-practices) - - [8.1 **Password generation**](#81-password-generation) - - [8.2 **File permissions**](#82-file-permissions) - - [9. **Service Configuration**](#9-service-configuration) - - [9.1 **Configuration files**](#91-configuration-files) - - [9.2 **Credential management**](#92-credential-management) - - [9.3 **Enviroment files**](#93-enviroment-files) - - [9.4 **Services**](#94-services) - - [10. **Cleanup**](#10-cleanup) - - [10.1 **Remove temporary files**](#101-remove-temporary-files) - - [10.2 **Autoremove and autoclean**](#102-autoremove-and-autoclean) - - [11. **Best Practices Checklist**](#11-best-practices-checklist) - - [Example: High-Level Script Flow](#example-high-level-script-flow) +## πŸ“‹ Table of Contents -## 1. **File header** +- [Overview](#overview) +- [Execution Context](#execution-context) +- [File Structure](#file-structure) +- [Complete Script Template](#complete-script-template) +- [Installation Phases](#installation-phases) +- [Function Reference](#function-reference) +- [Best Practices](#best-practices) +- [Real Examples](#real-examples) +- [Troubleshooting](#troubleshooting) +- [Contribution Checklist](#contribution-checklist) -### 1.1 **Shebang** +--- -- Use `#!/usr/bin/env bash` as the shebang. +## Overview -```bash -#!/usr/bin/env bash +### Purpose + +Installation scripts (`install/AppName-install.sh`) **run inside the LXC container** and are responsible for: + +1. Setting up the container OS (updates, packages) +2. Installing application dependencies +3. Downloading and configuring the application +4. Setting up services and systemd units +5. Creating version tracking files for updates +6. Generating credentials/configurations +7. Final cleanup and validation + +### Key Characteristics + +- Runs as **root inside container** (not on Proxmox host) +- Executed automatically by `build_container()` from ct/AppName.sh +- Uses `$FUNCTIONS_FILE_PATH` for function library access +- Interactive elements via **whiptail** (GUI menus) +- Version-aware for update tracking + +### Execution Flow + +``` +ct/AppName.sh (Proxmox Host) + ↓ +build_container() + ↓ +pct exec CTID bash -c "$(cat install/AppName-install.sh)" + ↓ +install/AppName-install.sh (Inside Container) + ↓ +Container Ready with App Installed ``` -### 1.2 **Comments** +--- -- Add clear comments for script metadata, including author, copyright, and license information. -- Use meaningful inline comments to explain complex commands or logic. +## Execution Context -Example: +### Environment Variables Available ```bash +# From Proxmox/Container +CTID # Container ID (100, 101, etc.) +PCT_OSTYPE # OS type (alpine, debian, ubuntu) +HOSTNAME # Container hostname + +# From build.func +FUNCTIONS_FILE_PATH # Bash functions library (core.func + tools.func) +VERBOSE # Verbose mode (yes/no) +STD # Standard redirection variable (silent/empty) + +# From install.func +APP # Application name +NSAPP # Normalized app name (lowercase, no spaces) +METHOD # Installation method (ct/install) +RANDOM_UUID # Session UUID for telemetry +``` + +### Access to Functions + +```bash +# All functions from core.func available: +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color # ANSI colors +catch_errors # Error handling +msg_info # Display messages +msg_ok +msg_error + +# All functions from tools.func available: +setup_nodejs # Tool installation +setup_php +setup_python +setup_docker +# ... many more + +# All functions from install.func available: +motd_ssh # Final setup +customize +cleanup_lxc +``` + +--- + +## File Structure + +### Minimal install/AppName-install.sh Template + +```bash +#!/usr/bin/env bash # [1] Shebang + +# [2] Copyright/Metadata # Copyright (c) 2021-2025 community-scripts ORG -# Author: [YourUserName] -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: [SOURCE_URL] -``` +# Author: YourUsername +# License: MIT +# Source: https://example.com -> [!NOTE]: -> -> - Add your username -> - When updating/reworking scripts, add "| Co-Author [YourUserName]" - -### 1.3 **Variables and function import** - -- This sections adds the support for all needed functions and variables. - -```bash +# [3] Load functions source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 @@ -80,274 +130,992 @@ catch_errors setting_up_container network_check update_os + +# [4] Installation steps +msg_info "Installing Dependencies" +$STD apt-get install -y package1 package2 +msg_ok "Installed Dependencies" + +# [5] Final setup +motd_ssh +customize +cleanup_lxc ``` --- -## 2. **Variable naming and management** +## Complete Script Template -### 2.1 **Naming conventions** - -- Use uppercase names for constants and environment variables. -- Use lowercase names for local script variables. - -Example: +### Phase 1: Header & Initialization ```bash -DB_NAME=snipeit_db # Environment-like variable (constant) -db_user="snipeit" # Local variable +#!/usr/bin/env bash +# Copyright (c) 2021-2025 community-scripts ORG +# Author: YourUsername +# Co-Author: AnotherAuthor (for updates) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/application/repo + +# Load all available functions (from core.func + tools.func) +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" + +# Initialize environment +color # Setup ANSI colors and icons +verb_ip6 # Configure IPv6 (if needed) +catch_errors # Setup error traps +setting_up_container # Verify OS is ready +network_check # Verify internet connectivity +update_os # Update packages (apk/apt) ``` ---- - -## 3. **Dependencies** - -### 3.1 **Install all at once** - -- Install all dependencies with a single command if possible - -Example: - -```bash -$STD apt-get install -y \ - curl \ - composer \ - git \ - sudo \ - mc \ - nginx -``` - -### 3.2 **Collapse dependencies** - -Collapse dependencies to keep the code readable. - -Example: -Use - -```bash -php8.2-{bcmath,common,ctype} -``` - -instead of - -```bash -php8.2-bcmath php8.2-common php8.2-ctype -``` - ---- - -## 4. **Paths to application files** - -If possible install the app and all necessary files in `/opt/` - ---- - -## 5. **Version management** - -### 5.1 **Install the latest release** - -- Always try and install the latest release -- Do not hardcode any version if not absolutely necessary - -Example for a git release: - -```bash -RELEASE=$(curl -fsSL https://api.github.com/repos/snipe/snipe-it/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -wget -q "https://github.com/snipe/snipe-it/archive/refs/tags/v${RELEASE}.zip" -``` - -### 5.2 **Save the version for update checks** - -- Write the installed version into a file. -- This is used for the update function in **AppName.sh** to check for if a Update is needed. - -Example: - -```bash -echo "${RELEASE}" >"/opt/AppName_version.txt" -``` - ---- - -## 6. **Input and output management** - -### 6.1 **User feedback** - -- Use standard functions like `msg_info`, `msg_ok` or `msg_error` to print status messages. -- Each `msg_info` must be followed with a `msg_ok` before any other output is made. -- Display meaningful progress messages at key stages. - -Example: +### Phase 2: Dependency Installation ```bash msg_info "Installing Dependencies" -$STD apt-get install -y ... +$STD apt-get install -y \ + curl \ + wget \ + git \ + nano \ + build-essential \ + libssl-dev \ + python3-dev msg_ok "Installed Dependencies" ``` -### 6.2 **Verbosity** +**Guidelines**: +- Use `\` for line continuation (readability) +- Group related packages together +- Collapse repeated prefixes: `php8.4-{bcmath,curl,gd,intl,mbstring}` +- Use `-y` flag for non-interactive installation +- Silence output with `$STD` unless debugging -- Use the appropiate flag (**-q** in the examples) for a command to suppres its output -Example: +### Phase 3: Tool Setup (Using tools.func) ```bash -wget -q -unzip -q +# Setup specific tool versions +NODE_VERSION="22" setup_nodejs + +# Or for databases +MYSQL_VERSION="8.0" setup_mysql + +# Or for languages +PHP_VERSION="8.4" PHP_MODULE="redis,imagick" setup_php + +# Or for version control +setup_composer ``` -- If a command dose not come with such a functionality use `$STD` (a custom standard redirection variable) for managing output verbosity. - -Example: - +**Available Tool Functions**: ```bash -$STD apt-get install -y nginx +setup_nodejs # Node.js from official repo +setup_php # PHP with optional modules +setup_python # Python 3 +setup_mariadb # MariaDB database +setup_mysql # MySQL database +setup_postgresql # PostgreSQL database +setup_mongodb # MongoDB database +setup_docker # Docker Engine +setup_nodejs # Node.js runtime +setup_composer # PHP Composer +setup_ruby # Ruby runtime +setup_rust # Rust toolchain +setup_go # Go language +setup_java # Java/Temurin +# ... many more in tools.func.md ``` ---- - -## 7. **String/File Manipulation** - -### 7.1 **File Manipulation** - -- Use `sed` to replace placeholder values in configuration files. - -Example: +### Phase 4: Application Download & Setup ```bash -sed -i -e "s|^DB_DATABASE=.*|DB_DATABASE=$DB_NAME|" \ - -e "s|^DB_USERNAME=.*|DB_USERNAME=$DB_USER|" \ - -e "s|^DB_PASSWORD=.*|DB_PASSWORD=$DB_PASS|" .env +# Method A: Download from GitHub releases +msg_info "Downloading ${APP}" +RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \ + grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') + +wget -q "https://github.com/user/repo/releases/download/v${RELEASE}/app-${RELEASE}.tar.gz" \ + -O /opt/app-${RELEASE}.tar.gz + +cd /opt +tar -xzf app-${RELEASE}.tar.gz +rm -f app-${RELEASE}.tar.gz +msg_ok "Downloaded and extracted ${APP}" + +# Method B: Clone from Git +git clone https://github.com/user/repo /opt/appname + +# Method C: Download single file +fetch_and_deploy_gh_release "AppName" "user/repo" "tarball" ``` ---- - -## 8. **Security practices** - -### 8.1 **Password generation** - -- Use `openssl` to generate random passwords. -- Use only alphanumeric values to not introduce unknown behaviour. - -Example: +### Phase 5: Configuration Files ```bash -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -``` - -### 8.2 **File permissions** - -Explicitly set secure ownership and permissions for sensitive files. - -Example: - -```bash -chown -R www-data: /opt/snipe-it -chmod -R 755 /opt/snipe-it -``` - ---- - -## 9. **Service Configuration** - -### 9.1 **Configuration files** - -Use `cat </etc/nginx/conf.d/snipeit.conf +# Method A: Using cat << EOF (multiline) +cat <<'EOF' >/etc/nginx/sites-available/appname server { listen 80; - root /opt/snipe-it/public; - index index.php; + server_name _; + root /opt/appname/public; + index index.php index.html; + + location ~ \.php$ { + fastcgi_pass unix:/run/php-fpm.sock; + include fastcgi_params; + } } EOF + +# Method B: Using sed for replacements +sed -i -e "s|^DB_HOST=.*|DB_HOST=localhost|" \ + -e "s|^DB_USER=.*|DB_USER=appuser|" \ + /opt/appname/.env + +# Method C: Using echo for simple configs +echo "APP_KEY=base64:$(openssl rand -base64 32)" >> /opt/appname/.env ``` -### 9.2 **Credential management** - -Store the generated credentials in a file. - -Example: +### Phase 6: Database Setup (If Needed) ```bash -USERNAME=username -PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -{ - echo "Application-Credentials" - echo "Username: $USERNAME" - echo "Password: $PASSWORD" -} >> ~/application.creds +msg_info "Setting up Database" + +DB_NAME="appname_db" +DB_USER="appuser" +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) + +# For MySQL/MariaDB +mysql -u root <> ~/appname.creds +Database Credentials +Database: ${DB_NAME} +Username: ${DB_USER} +Password: ${DB_PASS} +EOF + +msg_ok "Database setup complete" ``` -### 9.3 **Enviroment files** - -Use `cat </path/to/.env -VARIABLE="value" -PORT=3000 -DB_NAME="${DB_NAME}" +msg_info "Setting permissions" + +# Web applications typically run as www-data +chown -R www-data:www-data /opt/appname +chmod -R 755 /opt/appname +chmod -R 644 /opt/appname/* +chmod 755 /opt/appname/*/.* + +# For apps with specific requirements +find /opt/appname/storage -type f -exec chmod 644 {} \; +find /opt/appname/storage -type d -exec chmod 755 {} \; + +msg_ok "Permissions set" +``` + +### Phase 8: Service Configuration + +```bash +# Enable systemd service +systemctl enable -q --now appname + +# Or for OpenRC (Alpine) +rc-service appname start +rc-update add appname default + +# Verify service is running +if systemctl is-active --quiet appname; then + msg_ok "Service running successfully" +else + msg_error "Service failed to start" + journalctl -u appname -n 20 + exit 1 +fi +``` + +### Phase 9: Version Tracking + +```bash +# Essential for update detection +echo "${RELEASE}" > /opt/${APP}_version.txt + +# Or with additional metadata +cat > /opt/${APP}_version.txt < /opt/${APP}_version.txt +``` + +### Phase 5: Configuration + +```bash +# Application-specific configuration +cat > /opt/appname/.env < /opt/appname/config.yml < /opt/${APP}_version.txt + +# ❌ Bad: No version file +# (Update function won't work) +``` + +#### 6. Handle Alpine vs Debian Differences + +```bash +# βœ… Good: Detect OS +if grep -qi 'alpine' /etc/os-release; then + apk add package +else + apt-get install -y package +fi + +# ❌ Bad: Assumes Debian +apt-get install -y package +``` + +#### 7. Use Proper Messaging + +```bash +# βœ… Good: Clear status progression +msg_info "Installing Dependencies" +$STD apt-get install -y package +msg_ok "Installed Dependencies" + +msg_info "Configuring Application" +# ... configuration ... +msg_ok "Application configured" + +# ❌ Bad: No status messages +apt-get install -y package +# ... configuration ... +``` + +### ❌ DON'T: + +#### 1. Hardcode Versions + +```bash +# ❌ Bad: Won't auto-update +VERSION="1.2.3" +wget https://example.com/app-1.2.3.tar.gz + +# βœ… Good: Fetch latest +RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | jq -r '.tag_name') +wget https://example.com/app-${RELEASE}.tar.gz +``` + +#### 2. Use Root Without Password + +```bash +# ❌ Bad: Allows unprompted root access +mysql -u root < /etc/systemd/system/appname.service < /opt/${APP}_version.txt + +motd_ssh +customize +cleanup_lxc +``` + +### Example 2: Database Application (PHP + MySQL) + +```bash +#!/usr/bin/env bash +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y git curl nginx supervisor +msg_ok "Installed Dependencies" + +msg_info "Setting up PHP" +PHP_VERSION="8.4" PHP_MODULE="bcmath,curl,gd,intl,mbstring,pdo_mysql,redis" setup_php +msg_ok "PHP installed" + +msg_info "Setting up Database" +MARIADB_VERSION="11.4" setup_mariadb +msg_ok "MariaDB installed" + +DB_NAME="appname_db" +DB_USER="appuser" +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) + +mysql -u root < /opt/${APP}_version.txt + +motd_ssh +customize +cleanup_lxc +``` + +--- + +## Troubleshooting + +### Installation Hangs + +**Symptom**: Script appears to freeze at particular step + +**Causes**: +1. Network connectivity lost +2. Repository server timing out +3. Interactive prompt waiting for input + +**Debug**: +```bash +# Check if process still running +ps aux | grep -i appname + +# Check network +ping -c 1 8.8.8.8 + +# Check apt lock +lsof /var/lib/apt/lists/lock +``` + +### Package Installation Fails + +**Symptom**: `E: Unable to locate package xyz` + +**Causes**: +1. Repository not updated +2. Package name incorrect for OS version +3. Conflicting repository configuration + +**Solution**: +```bash +# Force update +apt-get update --allow-releaseinfo-change +apt-cache search package | grep exact_name +``` + +### Permission Denied on Files + +**Symptom**: Application can't write to `/opt/appname` + +**Causes**: +1. Wrong owner +2. Wrong permissions (644 for files, 755 for directories) + +**Fix**: +```bash +chown -R www-data:www-data /opt/appname +chmod -R 755 /opt/appname +find /opt/appname -type f -exec chmod 644 {} \; +find /opt/appname -type d -exec chmod 755 {} \; +``` + +### Service Won't Start + +**Symptom**: `systemctl status appname` shows failed + +**Debug**: +```bash +# Check service status +systemctl status appname + +# View logs +journalctl -u appname -n 50 + +# Check configuration +systemctl cat appname +``` + +--- + +## Contribution Checklist + +Before submitting a PR: + +### Script Structure +- [ ] Shebang is `#!/usr/bin/env bash` +- [ ] Copyright header with author and source URL +- [ ] Functions loaded via `$FUNCTIONS_FILE_PATH` +- [ ] Initial setup: `color`, `catch_errors`, `setting_up_container`, `network_check`, `update_os` + +### Installation Flow +- [ ] Dependencies installed with `$STD apt-get install -y \` +- [ ] Package names collapsed (`php-{bcmath,curl}`) +- [ ] Tool setup uses functions from tools.func (not manual installation) +- [ ] Application version fetched dynamically (not hardcoded) +- [ ] Version saved to `/opt/${APP}_version.txt` + +### Configuration +- [ ] Configuration files created properly (heredoc or sed) +- [ ] Credentials generated randomly (`openssl rand`) +- [ ] Credentials stored in creds file +- [ ] Passwords use alphanumeric only (no special chars) +- [ ] Proper file permissions set + +### Messaging +- [ ] `msg_info` followed by action then `msg_ok` +- [ ] Error cases use `msg_error` and exit +- [ ] No bare `echo` statements for status (use msg_* functions) + +### Cleanup +- [ ] Temporary files removed +- [ ] Package manager cache cleaned (`autoremove`, `autoclean`) +- [ ] `cleanup_lxc` called at end +- [ ] `motd_ssh` called before `customize` +- [ ] `customize` called before exit + +### Testing +- [ ] Script tested with default OS (Debian 12/Ubuntu 22.04) +- [ ] Script tested with Alpine (if applicable) +- [ ] Script tested with verbose mode (`VERBOSE=yes`) +- [ ] Error handling tested (network interruption, missing packages) +- [ ] Cleanup verified (disk space reduced, temp files removed) + +--- + +## Related Documentation + +- [ct/AppName.sh Guide](UPDATED_APP-ct.md) +- [tools.func Wiki](../misc/tools.func.md) +- [install.func Wiki](../misc/install.func.md) +- [error_handler.func Wiki](../misc/error_handler.func.md) + +--- + +**Last Updated**: December 2025 +**Compatibility**: ProxmoxVED with tools.func v2+ +**Questions?** Open an issue in the repository diff --git a/docs/UPDATED_APP-ct.md b/docs/UPDATED_APP-ct.md deleted file mode 100644 index e7a12ea7e..000000000 --- a/docs/UPDATED_APP-ct.md +++ /dev/null @@ -1,717 +0,0 @@ -# πŸš€ **Application Container Scripts (ct/AppName.sh)** - -**Modern Guide to Creating LXC Container Installation Scripts** - -> **Updated**: December 2025 -> **Context**: Fully integrated with build.func, advanced_settings wizard, and defaults system -> **Example Used**: `/ct/pihole.sh`, `/ct/docker.sh` - ---- - -## πŸ“‹ Table of Contents - -- [Overview](#overview) -- [Architecture & Flow](#architecture--flow) -- [File Structure](#file-structure) -- [Complete Script Template](#complete-script-template) -- [Function Reference](#function-reference) -- [Advanced Features](#advanced-features) -- [Real Examples](#real-examples) -- [Troubleshooting](#troubleshooting) -- [Contribution Checklist](#contribution-checklist) - ---- - -## Overview - -### Purpose - -Container scripts (`ct/AppName.sh`) are **entry points for creating LXC containers** with specific applications pre-installed. They: - -1. Define container defaults (CPU, RAM, disk, OS) -2. Call the main build orchestrator (`build.func`) -3. Implement application-specific update mechanisms -4. Provide user-facing success messages - -### Execution Context - -``` -Proxmox Host - ↓ -ct/AppName.sh sourced (runs as root on host) - ↓ -build.func: Creates LXC container + runs install script inside - ↓ -install/AppName-install.sh (runs inside container) - ↓ -Container ready with app installed -``` - -### Key Integration Points - -- **build.func** - Main orchestrator (container creation, storage, variable management) -- **install.func** - Container-specific setup (OS update, package management) -- **tools.func** - Tool installation helpers (repositories, GitHub releases) -- **core.func** - UI/messaging functions (colors, spinners, validation) -- **error_handler.func** - Error handling and signal management - ---- - -## Architecture & Flow - -### Container Creation Flow - -``` -START: bash ct/pihole.sh - ↓ -[1] Set APP, var_*, defaults - ↓ -[2] header_info() β†’ Display ASCII art - ↓ -[3] variables() β†’ Parse arguments & load build.func - ↓ -[4] color() β†’ Setup ANSI codes - ↓ -[5] catch_errors() β†’ Setup trap handlers - ↓ -[6] install_script() β†’ Show mode menu (5 options) - ↓ - β”œβ”€ INSTALL_MODE="0" (Default) - β”œβ”€ INSTALL_MODE="1" (Advanced - 19-step wizard) - β”œβ”€ INSTALL_MODE="2" (User Defaults) - β”œβ”€ INSTALL_MODE="3" (App Defaults) - └─ INSTALL_MODE="4" (Settings Menu) - ↓ -[7] advanced_settings() β†’ Collect user configuration (if mode=1) - ↓ -[8] start() β†’ Confirm or re-edit settings - ↓ -[9] build_container() β†’ Create LXC + execute install script - ↓ -[10] description() β†’ Set container description - ↓ -[11] SUCCESS β†’ Display access URL - ↓ -END -``` - -### Default Values Precedence - -``` -Priority 1 (Highest): Environment Variables (var_cpu, var_ram, etc.) -Priority 2: App-Specific Defaults (/defaults/AppName.vars) -Priority 3: User Global Defaults (/default.vars) -Priority 4 (Lowest): Built-in Defaults (in build.func) -``` - ---- - -## File Structure - -### Minimal ct/AppName.sh Template - -``` -#!/usr/bin/env bash # [1] Shebang - # [2] Copyright/License -source <(curl -s .../misc/build.func) # [3] Import functions - # [4] APP metadata -APP="AppName" # [5] Default values -var_tags="tag1;tag2" -var_cpu="2" -var_ram="2048" -... - -header_info "$APP" # [6] Display header -variables # [7] Process arguments -color # [8] Setup colors -catch_errors # [9] Setup error handling - -function update_script() { ... } # [10] Update function (optional) - -start # [11] Launch container creation -build_container -description -msg_ok "Completed Successfully!\n" -``` - ---- - -## Complete Script Template - -### 1. File Header & Imports - -```bash -#!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG -# Author: YourUsername -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/example/project - -# Import main orchestrator -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -``` - -> **⚠️ IMPORTANT**: Before opening a PR, change URL to `community-scripts` repo! - -### 2. Application Metadata - -```bash -# Application Configuration -APP="ApplicationName" -var_tags="tag1;tag2;tag3" # Max 3-4 tags, no spaces, semicolon-separated - -# Container Resources -var_cpu="2" # CPU cores -var_ram="2048" # RAM in MB -var_disk="10" # Disk in GB - -# Container Type & OS -var_os="debian" # Options: alpine, debian, ubuntu -var_version="12" # Alpine: 3.20+, Debian: 11-13, Ubuntu: 20.04+ -var_unprivileged="1" # 1=unprivileged (secure), 0=privileged (rarely needed) -``` - -**Variable Naming Convention**: -- Variables exposed to user: `var_*` (e.g., `var_cpu`, `var_hostname`, `var_ssh`) -- Internal variables: lowercase (e.g., `container_id`, `app_version`) - -### 3. Display & Initialization - -```bash -# Display header ASCII art -header_info "$APP" - -# Process command-line arguments and load configuration -variables - -# Setup ANSI color codes and formatting -color - -# Initialize error handling (trap ERR, EXIT, INT, TERM) -catch_errors -``` - -### 4. Update Function (Highly Recommended) - -```bash -function update_script() { - header_info - - # Always start with these checks - check_container_storage - check_container_resources - - # Verify app is installed - if [[ ! -d /opt/appname ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - # Get latest version from GitHub - RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \ - grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') - - # Compare with saved version - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Updating ${APP} to v${RELEASE}" - - # Backup user data - cp -r /opt/appname /opt/appname-backup - - # Perform update - cd /opt - wget -q "https://github.com/user/repo/releases/download/v${RELEASE}/app-${RELEASE}.tar.gz" - tar -xzf app-${RELEASE}.tar.gz - - # Restore user data - cp /opt/appname-backup/config/* /opt/appname/config/ - - # Cleanup - rm -rf app-${RELEASE}.tar.gz /opt/appname-backup - - # Save new version - echo "${RELEASE}" > /opt/${APP}_version.txt - - msg_ok "Updated ${APP} to v${RELEASE}" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}." - fi - - exit -} -``` - -### 5. Script Launch - -```bash -# Start the container creation workflow -start - -# Build the container with selected configuration -build_container - -# Set container description/notes in Proxmox UI -description - -# Display success message -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" -``` - ---- - -## Function Reference - -### Core Functions (From build.func) - -#### `variables()` - -**Purpose**: Initialize container variables, load user arguments, setup orchestration - -**Triggered by**: Called automatically at script start - -**Behavior**: -1. Parse command-line arguments (if any) -2. Generate random UUID for session tracking -3. Load container storage from Proxmox -4. Initialize application-specific defaults -5. Setup SSH/environment configuration - -**Example Output**: -``` -Setting up variables... -Container ID: 100 -Storage: local-lvm -Install method: Default -``` - -#### `start()` - -**Purpose**: Launch the container creation menu with 5 installation modes - -**Triggered by**: Called just before `build_container()` - -**Menu Options**: -``` -1. Default Installation (Quick setup, predefined settings) -2. Advanced Installation (19-step wizard with full control) -3. User Defaults (Load ~/.community-scripts/default.vars) -4. App Defaults (Load /defaults/AppName.vars) -5. Settings Menu (Interactive mode selection) -``` - -**User Flow**: -``` -Select installation mode: -1) Default -2) Advanced -3) User Defaults -4) App Defaults -5) Settings Menu -Enter choice: 2 -``` - -#### `build_container()` - -**Purpose**: Main orchestrator for LXC container creation - -**Operations**: -1. Validates all variables -2. Creates LXC container via `pct create` -3. Executes `install/AppName-install.sh` inside container -4. Monitors installation progress -5. Handles errors and rollback on failure - -**Exit Codes**: -- `0` - Success -- `1-255` - Various error conditions (see error_handler.func) - -#### `description()` - -**Purpose**: Set container description/notes visible in Proxmox UI - -**Format**: -``` -AppName -IP: [IP] -Version: [Version] -Tags: [Tags] -``` - -#### `header_info()` - -**Purpose**: Display ASCII art header for application - -**Sources**: -- Tries `/usr/local/community-scripts/headers/ct/appname` (cached) -- Falls back to remote fetch from GitHub -- Returns silently if not found - ---- - -## Advanced Features - -### 1. Integration with Defaults System - -#### Save App Defaults After Installation - -```bash -# At end of install script, after successful setup: -maybe_offer_save_app_defaults - -# Output: -# "Save these settings as App Defaults for AppName? (Y/n)" -# Yes β†’ Saves to /defaults/appname.vars -# No β†’ Skips saving -``` - -#### Load Saved Defaults During Container Creation - -```bash -# In ct/AppName.sh, user selects "App Defaults" mode -# Automatically loads /defaults/appname.vars -# Container uses previously saved configuration -``` - -### 2. Custom Configuration Menus - -If your app has additional setup beyond standard vars: - -```bash -# In ct/AppName.sh, after variables() -custom_app_settings() { - CONFIGURE_DB=$(whiptail --title "Database Setup" \ - --yesno "Would you like to configure a custom database?" 8 60) - - if [[ $? -eq 0 ]]; then - DB_HOST=$(whiptail --inputbox "Database Host:" 8 60 3>&1 1>&2 2>&3) - DB_PORT=$(whiptail --inputbox "Database Port:" 8 60 "3306" 3>&1 1>&2 2>&3) - fi -} - -custom_app_settings -``` - -### 3. Version Tracking - -Save installed version for update checks: - -```bash -# In install script, after successful app download: -RELEASE="1.2.3" -echo "${RELEASE}" > /opt/${APP}_version.txt - -# In update function, compare: -CURRENT=$(cat /opt/${APP}_version.txt 2>/dev/null) -LATEST=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | jq -r '.tag_name') - -if [[ "$LATEST" != "$CURRENT" ]]; then - echo "Update available: $CURRENT β†’ $LATEST" -fi -``` - -### 4. Health Check Functions - -Add custom validation: - -```bash -function health_check() { - header_info - - if [[ ! -d /opt/appname ]]; then - msg_error "Application not found!" - exit 1 - fi - - if ! systemctl is-active --quiet appname; then - msg_error "Application service not running" - exit 1 - fi - - msg_ok "Health check passed" -} - -# Called via: bash ct/appname.sh health_check -``` - ---- - -## Real Examples - -### Example 1: Simple Web App (Debian-based) - -```bash -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) - -APP="Homarr" -var_tags="dashboard;homepage" -var_cpu="2" -var_ram="1024" -var_disk="5" -var_os="debian" -var_version="12" -var_unprivileged="1" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/homarr ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/ajnart/homarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') - - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Updating ${APP} to v${RELEASE}" - systemctl stop homarr - - cd /opt/homarr - wget -q "https://github.com/ajnart/homarr/releases/download/v${RELEASE}/docker-compose.yml" - docker-compose up -d - - echo "${RELEASE}" > /opt/${APP}_version.txt - msg_ok "Updated ${APP} to v${RELEASE}" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}." - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5100${CL}" -``` - -### Example 2: Database App (Alpine-based) - -```bash -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) - -APP="PostgreSQL" -var_tags="database;sql" -var_cpu="4" -var_ram="4096" -var_disk="20" -var_os="alpine" -var_version="3.20" -var_unprivileged="1" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - - if ! command -v psql &>/dev/null; then - msg_error "PostgreSQL not installed!" - exit - fi - - msg_info "Updating Alpine packages" - apk update - apk upgrade - msg_ok "Updated Alpine packages" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Connect using:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}psql -h ${IP} -U postgres${CL}" -``` - ---- - -## Troubleshooting - -### Container Creation Fails - -**Symptom**: `pct create` exits with error code 209 - -**Causes**: -1. CTID already exists: `pct list` shows duplicate -2. Storage full: Check storage space -3. Network template unavailable - -**Solution**: -```bash -# Check existing containers -pct list | grep CTID - -# Remove conflicting container -pct destroy CTID - -# Retry ct/AppName.sh -``` - -### Update Function Doesn't Detect New Version - -**Symptom**: Update available but script says "already at latest" - -**Causes**: -1. Version file missing: `/opt/AppName_version.txt` -2. GitHub API rate limit exceeded -3. Release tag format mismatch - -**Debug**: -```bash -# Check version file -cat /opt/AppName_version.txt - -# Test GitHub API -curl -fsSL https://api.github.com/repos/user/repo/releases/latest | grep tag_name - -# Inside container -bash ct/appname.sh update_script -``` - -### Header ASCII Art Not Displaying - -**Symptom**: Container script runs but no header shown - -**Causes**: -1. Header file not in repository -2. Caching issue - -**Solution**: -```bash -# Create header file manually -mkdir -p /usr/local/community-scripts/headers/ct -echo "Your ASCII art here" > /usr/local/community-scripts/headers/ct/appname - -# Or remove cache to force re-download -rm -f /usr/local/community-scripts/headers/ct/appname -``` - ---- - -## Contribution Checklist - -Before submitting a PR: - -### Script Structure -- [ ] Shebang is `#!/usr/bin/env bash` -- [ ] Imports `build.func` from community-scripts repo (not personal fork) -- [ ] Copyright header with author and source URL -- [ ] APP variable matches filename -- [ ] `var_tags` are semicolon-separated (no spaces) - -### Default Values -- [ ] `var_cpu` set appropriately (2-4 for most apps) -- [ ] `var_ram` set appropriately (1024-4096 MB minimum) -- [ ] `var_disk` sufficient for app + data (5-20 GB) -- [ ] `var_os` is realistic (Alpine if lightweight, Debian/Ubuntu otherwise) -- [ ] `var_unprivileged="1"` unless app absolutely needs privileges - -### Functions -- [ ] `update_script()` implemented (or marked as unavailable) -- [ ] Update function checks if app installed -- [ ] Update function checks for new version -- [ ] Update function performs cleanup after update -- [ ] Proper error handling with `msg_error` on failure - -### Output -- [ ] Success message displayed with access URL -- [ ] URL format: `http://IP:PORT/path` (if web-based) -- [ ] Uses `msg_ok`, `msg_info`, `msg_error` for feedback - -### Testing -- [ ] Script tested with default installation -- [ ] Script tested with advanced (19-step) installation -- [ ] Update function tested on existing installation -- [ ] Error handling tested (invalid settings, network issues) - ---- - -## Best Practices - -### βœ… DO: - -1. **Use meaningful defaults** - ```bash - var_cpu="2" # βœ… Good: Typical workload - var_cpu="128" # ❌ Bad: Unrealistic - ``` - -2. **Implement version tracking** - ```bash - echo "${RELEASE}" > /opt/${APP}_version.txt # βœ… Good - # ❌ Bad: No version tracking - ``` - -3. **Handle edge cases** - ```bash - if [[ ! -f /opt/${APP}_version.txt ]]; then - msg_info "First installation detected" - fi - ``` - -4. **Use proper messaging** - ```bash - msg_info "Updating..." # βœ… Good: Clear status - echo "Updating..." # ❌ Bad: No formatting - ``` - -### ❌ DON'T: - -1. **Hardcode versions** - ```bash - RELEASE="1.2.3" # ❌ Bad: Won't auto-update - ``` - -2. **Use custom color codes** - ```bash - echo -e "\033[32mSuccess" # ❌ Bad: Use $GN instead - ``` - -3. **Forget error handling** - ```bash - wget file.zip # ❌ Bad: No error check - if ! wget -q file.zip; then # βœ… Good - msg_error "Download failed" - fi - ``` - -4. **Leave temporary files** - ```bash - rm -rf /opt/file.zip # βœ… Always cleanup - ``` - ---- - -## Related Documentation - -- [install/AppName-install.sh Guide](UPDATED_APP-install.md) -- [build.func Wiki](../misc/build.func.md) -- [tools.func Wiki](../misc/tools.func.md) -- [Defaults System Guide](../DEFAULTS_SYSTEM_GUIDE.md) - ---- - -**Last Updated**: December 2025 -**Compatibility**: ProxmoxVED with build.func v3+ -**Questions?** Open an issue in the repository diff --git a/docs/UPDATED_APP-install.md b/docs/UPDATED_APP-install.md deleted file mode 100644 index c012aba5c..000000000 --- a/docs/UPDATED_APP-install.md +++ /dev/null @@ -1,1121 +0,0 @@ -# πŸ› οΈ **Application Installation Scripts (install/AppName-install.sh)** - -**Modern Guide to Writing In-Container Installation Scripts** - -> **Updated**: December 2025 -> **Context**: Integrated with tools.func, error_handler.func, and install.func -> **Examples Used**: `/install/pihole-install.sh`, `/install/mealie-install.sh` - ---- - -## πŸ“‹ Table of Contents - -- [Overview](#overview) -- [Execution Context](#execution-context) -- [File Structure](#file-structure) -- [Complete Script Template](#complete-script-template) -- [Installation Phases](#installation-phases) -- [Function Reference](#function-reference) -- [Best Practices](#best-practices) -- [Real Examples](#real-examples) -- [Troubleshooting](#troubleshooting) -- [Contribution Checklist](#contribution-checklist) - ---- - -## Overview - -### Purpose - -Installation scripts (`install/AppName-install.sh`) **run inside the LXC container** and are responsible for: - -1. Setting up the container OS (updates, packages) -2. Installing application dependencies -3. Downloading and configuring the application -4. Setting up services and systemd units -5. Creating version tracking files for updates -6. Generating credentials/configurations -7. Final cleanup and validation - -### Key Characteristics - -- Runs as **root inside container** (not on Proxmox host) -- Executed automatically by `build_container()` from ct/AppName.sh -- Uses `$FUNCTIONS_FILE_PATH` for function library access -- Interactive elements via **whiptail** (GUI menus) -- Version-aware for update tracking - -### Execution Flow - -``` -ct/AppName.sh (Proxmox Host) - ↓ -build_container() - ↓ -pct exec CTID bash -c "$(cat install/AppName-install.sh)" - ↓ -install/AppName-install.sh (Inside Container) - ↓ -Container Ready with App Installed -``` - ---- - -## Execution Context - -### Environment Variables Available - -```bash -# From Proxmox/Container -CTID # Container ID (100, 101, etc.) -PCT_OSTYPE # OS type (alpine, debian, ubuntu) -HOSTNAME # Container hostname - -# From build.func -FUNCTIONS_FILE_PATH # Bash functions library (core.func + tools.func) -VERBOSE # Verbose mode (yes/no) -STD # Standard redirection variable (silent/empty) - -# From install.func -APP # Application name -NSAPP # Normalized app name (lowercase, no spaces) -METHOD # Installation method (ct/install) -RANDOM_UUID # Session UUID for telemetry -``` - -### Access to Functions - -```bash -# All functions from core.func available: -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color # ANSI colors -catch_errors # Error handling -msg_info # Display messages -msg_ok -msg_error - -# All functions from tools.func available: -setup_nodejs # Tool installation -setup_php -setup_python -setup_docker -# ... many more - -# All functions from install.func available: -motd_ssh # Final setup -customize -cleanup_lxc -``` - ---- - -## File Structure - -### Minimal install/AppName-install.sh Template - -```bash -#!/usr/bin/env bash # [1] Shebang - -# [2] Copyright/Metadata -# Copyright (c) 2021-2025 community-scripts ORG -# Author: YourUsername -# License: MIT -# Source: https://example.com - -# [3] Load functions -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -# [4] Installation steps -msg_info "Installing Dependencies" -$STD apt-get install -y package1 package2 -msg_ok "Installed Dependencies" - -# [5] Final setup -motd_ssh -customize -cleanup_lxc -``` - ---- - -## Complete Script Template - -### Phase 1: Header & Initialization - -```bash -#!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG -# Author: YourUsername -# Co-Author: AnotherAuthor (for updates) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/application/repo - -# Load all available functions (from core.func + tools.func) -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" - -# Initialize environment -color # Setup ANSI colors and icons -verb_ip6 # Configure IPv6 (if needed) -catch_errors # Setup error traps -setting_up_container # Verify OS is ready -network_check # Verify internet connectivity -update_os # Update packages (apk/apt) -``` - -### Phase 2: Dependency Installation - -```bash -msg_info "Installing Dependencies" -$STD apt-get install -y \ - curl \ - wget \ - git \ - nano \ - build-essential \ - libssl-dev \ - python3-dev -msg_ok "Installed Dependencies" -``` - -**Guidelines**: -- Use `\` for line continuation (readability) -- Group related packages together -- Collapse repeated prefixes: `php8.4-{bcmath,curl,gd,intl,mbstring}` -- Use `-y` flag for non-interactive installation -- Silence output with `$STD` unless debugging - -### Phase 3: Tool Setup (Using tools.func) - -```bash -# Setup specific tool versions -NODE_VERSION="22" setup_nodejs - -# Or for databases -MYSQL_VERSION="8.0" setup_mysql - -# Or for languages -PHP_VERSION="8.4" PHP_MODULE="redis,imagick" setup_php - -# Or for version control -setup_composer -``` - -**Available Tool Functions**: -```bash -setup_nodejs # Node.js from official repo -setup_php # PHP with optional modules -setup_python # Python 3 -setup_mariadb # MariaDB database -setup_mysql # MySQL database -setup_postgresql # PostgreSQL database -setup_mongodb # MongoDB database -setup_docker # Docker Engine -setup_nodejs # Node.js runtime -setup_composer # PHP Composer -setup_ruby # Ruby runtime -setup_rust # Rust toolchain -setup_go # Go language -setup_java # Java/Temurin -# ... many more in tools.func.md -``` - -### Phase 4: Application Download & Setup - -```bash -# Method A: Download from GitHub releases -msg_info "Downloading ${APP}" -RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \ - grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') - -wget -q "https://github.com/user/repo/releases/download/v${RELEASE}/app-${RELEASE}.tar.gz" \ - -O /opt/app-${RELEASE}.tar.gz - -cd /opt -tar -xzf app-${RELEASE}.tar.gz -rm -f app-${RELEASE}.tar.gz -msg_ok "Downloaded and extracted ${APP}" - -# Method B: Clone from Git -git clone https://github.com/user/repo /opt/appname - -# Method C: Download single file -fetch_and_deploy_gh_release "AppName" "user/repo" "tarball" -``` - -### Phase 5: Configuration Files - -```bash -# Method A: Using cat << EOF (multiline) -cat <<'EOF' >/etc/nginx/sites-available/appname -server { - listen 80; - server_name _; - root /opt/appname/public; - index index.php index.html; - - location ~ \.php$ { - fastcgi_pass unix:/run/php-fpm.sock; - include fastcgi_params; - } -} -EOF - -# Method B: Using sed for replacements -sed -i -e "s|^DB_HOST=.*|DB_HOST=localhost|" \ - -e "s|^DB_USER=.*|DB_USER=appuser|" \ - /opt/appname/.env - -# Method C: Using echo for simple configs -echo "APP_KEY=base64:$(openssl rand -base64 32)" >> /opt/appname/.env -``` - -### Phase 6: Database Setup (If Needed) - -```bash -msg_info "Setting up Database" - -DB_NAME="appname_db" -DB_USER="appuser" -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) - -# For MySQL/MariaDB -mysql -u root <> ~/appname.creds -Database Credentials -Database: ${DB_NAME} -Username: ${DB_USER} -Password: ${DB_PASS} -EOF - -msg_ok "Database setup complete" -``` - -### Phase 7: Permission & Ownership - -```bash -msg_info "Setting permissions" - -# Web applications typically run as www-data -chown -R www-data:www-data /opt/appname -chmod -R 755 /opt/appname -chmod -R 644 /opt/appname/* -chmod 755 /opt/appname/*/.* - -# For apps with specific requirements -find /opt/appname/storage -type f -exec chmod 644 {} \; -find /opt/appname/storage -type d -exec chmod 755 {} \; - -msg_ok "Permissions set" -``` - -### Phase 8: Service Configuration - -```bash -# Enable systemd service -systemctl enable -q --now appname - -# Or for OpenRC (Alpine) -rc-service appname start -rc-update add appname default - -# Verify service is running -if systemctl is-active --quiet appname; then - msg_ok "Service running successfully" -else - msg_error "Service failed to start" - journalctl -u appname -n 20 - exit 1 -fi -``` - -### Phase 9: Version Tracking - -```bash -# Essential for update detection -echo "${RELEASE}" > /opt/${APP}_version.txt - -# Or with additional metadata -cat > /opt/${APP}_version.txt < /opt/${APP}_version.txt -``` - -### Phase 5: Configuration - -```bash -# Application-specific configuration -cat > /opt/appname/.env < /opt/appname/config.yml < /opt/${APP}_version.txt - -# ❌ Bad: No version file -# (Update function won't work) -``` - -#### 6. Handle Alpine vs Debian Differences - -```bash -# βœ… Good: Detect OS -if grep -qi 'alpine' /etc/os-release; then - apk add package -else - apt-get install -y package -fi - -# ❌ Bad: Assumes Debian -apt-get install -y package -``` - -#### 7. Use Proper Messaging - -```bash -# βœ… Good: Clear status progression -msg_info "Installing Dependencies" -$STD apt-get install -y package -msg_ok "Installed Dependencies" - -msg_info "Configuring Application" -# ... configuration ... -msg_ok "Application configured" - -# ❌ Bad: No status messages -apt-get install -y package -# ... configuration ... -``` - -### ❌ DON'T: - -#### 1. Hardcode Versions - -```bash -# ❌ Bad: Won't auto-update -VERSION="1.2.3" -wget https://example.com/app-1.2.3.tar.gz - -# βœ… Good: Fetch latest -RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | jq -r '.tag_name') -wget https://example.com/app-${RELEASE}.tar.gz -``` - -#### 2. Use Root Without Password - -```bash -# ❌ Bad: Allows unprompted root access -mysql -u root < /etc/systemd/system/appname.service < /opt/${APP}_version.txt - -motd_ssh -customize -cleanup_lxc -``` - -### Example 2: Database Application (PHP + MySQL) - -```bash -#!/usr/bin/env bash -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y git curl nginx supervisor -msg_ok "Installed Dependencies" - -msg_info "Setting up PHP" -PHP_VERSION="8.4" PHP_MODULE="bcmath,curl,gd,intl,mbstring,pdo_mysql,redis" setup_php -msg_ok "PHP installed" - -msg_info "Setting up Database" -MARIADB_VERSION="11.4" setup_mariadb -msg_ok "MariaDB installed" - -DB_NAME="appname_db" -DB_USER="appuser" -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) - -mysql -u root < /opt/${APP}_version.txt - -motd_ssh -customize -cleanup_lxc -``` - ---- - -## Troubleshooting - -### Installation Hangs - -**Symptom**: Script appears to freeze at particular step - -**Causes**: -1. Network connectivity lost -2. Repository server timing out -3. Interactive prompt waiting for input - -**Debug**: -```bash -# Check if process still running -ps aux | grep -i appname - -# Check network -ping -c 1 8.8.8.8 - -# Check apt lock -lsof /var/lib/apt/lists/lock -``` - -### Package Installation Fails - -**Symptom**: `E: Unable to locate package xyz` - -**Causes**: -1. Repository not updated -2. Package name incorrect for OS version -3. Conflicting repository configuration - -**Solution**: -```bash -# Force update -apt-get update --allow-releaseinfo-change -apt-cache search package | grep exact_name -``` - -### Permission Denied on Files - -**Symptom**: Application can't write to `/opt/appname` - -**Causes**: -1. Wrong owner -2. Wrong permissions (644 for files, 755 for directories) - -**Fix**: -```bash -chown -R www-data:www-data /opt/appname -chmod -R 755 /opt/appname -find /opt/appname -type f -exec chmod 644 {} \; -find /opt/appname -type d -exec chmod 755 {} \; -``` - -### Service Won't Start - -**Symptom**: `systemctl status appname` shows failed - -**Debug**: -```bash -# Check service status -systemctl status appname - -# View logs -journalctl -u appname -n 50 - -# Check configuration -systemctl cat appname -``` - ---- - -## Contribution Checklist - -Before submitting a PR: - -### Script Structure -- [ ] Shebang is `#!/usr/bin/env bash` -- [ ] Copyright header with author and source URL -- [ ] Functions loaded via `$FUNCTIONS_FILE_PATH` -- [ ] Initial setup: `color`, `catch_errors`, `setting_up_container`, `network_check`, `update_os` - -### Installation Flow -- [ ] Dependencies installed with `$STD apt-get install -y \` -- [ ] Package names collapsed (`php-{bcmath,curl}`) -- [ ] Tool setup uses functions from tools.func (not manual installation) -- [ ] Application version fetched dynamically (not hardcoded) -- [ ] Version saved to `/opt/${APP}_version.txt` - -### Configuration -- [ ] Configuration files created properly (heredoc or sed) -- [ ] Credentials generated randomly (`openssl rand`) -- [ ] Credentials stored in creds file -- [ ] Passwords use alphanumeric only (no special chars) -- [ ] Proper file permissions set - -### Messaging -- [ ] `msg_info` followed by action then `msg_ok` -- [ ] Error cases use `msg_error` and exit -- [ ] No bare `echo` statements for status (use msg_* functions) - -### Cleanup -- [ ] Temporary files removed -- [ ] Package manager cache cleaned (`autoremove`, `autoclean`) -- [ ] `cleanup_lxc` called at end -- [ ] `motd_ssh` called before `customize` -- [ ] `customize` called before exit - -### Testing -- [ ] Script tested with default OS (Debian 12/Ubuntu 22.04) -- [ ] Script tested with Alpine (if applicable) -- [ ] Script tested with verbose mode (`VERBOSE=yes`) -- [ ] Error handling tested (network interruption, missing packages) -- [ ] Cleanup verified (disk space reduced, temp files removed) - ---- - -## Related Documentation - -- [ct/AppName.sh Guide](UPDATED_APP-ct.md) -- [tools.func Wiki](../misc/tools.func.md) -- [install.func Wiki](../misc/install.func.md) -- [error_handler.func Wiki](../misc/error_handler.func.md) - ---- - -**Last Updated**: December 2025 -**Compatibility**: ProxmoxVED with tools.func v2+ -**Questions?** Open an issue in the repository diff --git a/docs/api.func.md b/docs/api.func.md new file mode 100644 index 000000000..17c635400 --- /dev/null +++ b/docs/api.func.md @@ -0,0 +1,670 @@ +# API.func Wiki + +A telemetry and diagnostics module providing anonymous statistics collection and API integration with the Community-Scripts infrastructure for tracking container/VM creation metrics and installation success/failure data. + +--- + +## πŸ“‹ Table of Contents + +- [Overview](#overview) +- [Exit Code Reference](#exit-code-reference) +- [Telemetry Functions](#telemetry-functions) +- [API Payload Structure](#api-payload-structure) +- [Privacy & Opt-Out](#privacy--opt-out) +- [Error Mapping](#error-mapping) +- [Best Practices](#best-practices) +- [API Integration](#api-integration) +- [Contributing](#contributing) + +--- + +## Overview + +The API.func module provides anonymous telemetry reporting to Community-Scripts infrastructure, enabling: + +- βœ… Container/VM creation statistics collection +- βœ… Installation success/failure tracking +- βœ… Comprehensive exit code mapping and explanation +- βœ… Anonymous session-based tracking (UUID) +- βœ… Privacy-respecting data collection (no personal data) +- βœ… Opt-out capability via DIAGNOSTICS setting +- βœ… Consistent error reporting across all scripts + +### Integration Points + +```bash +# In container build scripts (on Proxmox host): +source <(curl -fsSL .../api.func) +post_to_api # Report container creation +post_update_to_api # Report installation completion + +# Error handling (in all scripts): +source <(curl -fsSL .../error_handler.func) +# explain_exit_code shared for consistent mappings +``` + +### Data Flow + +``` +Container/VM Creation + ↓ + post_to_api() + ↓ +Community-Scripts API + ↓ +Anonymous Statistics +(No personal data) +``` + +--- + +## Exit Code Reference + +### Category 1: Generic / Shell Errors + +| Code | Meaning | Recovery | +|------|---------|----------| +| 1 | General error / Operation not permitted | Check permissions, re-run command | +| 2 | Misuse of shell builtins (syntax error) | Fix shell syntax, validate script | +| 126 | Command invoked cannot execute | Fix file permissions (chmod +x) | +| 127 | Command not found | Install missing package or tool | +| 128 | Invalid argument to exit | Check exit code parameter (0-255) | +| 130 | Terminated by Ctrl+C (SIGINT) | User interrupted - retry manually | +| 137 | Killed (SIGKILL / Out of memory) | Insufficient RAM - increase allocation | +| 139 | Segmentation fault (core dumped) | Serious application bug - contact support | +| 143 | Terminated (SIGTERM) | System shutdown or manual termination | + +### Category 2: Package Manager Errors + +| Code | Meaning | Recovery | +|------|---------|----------| +| 100 | APT: Package manager error (broken packages) | Run `apt --fix-broken install` | +| 101 | APT: Configuration error (bad sources.list) | Fix /etc/apt/sources.list, re-run apt update | +| 255 | DPKG: Fatal internal error | Run `dpkg --configure -a` | + +### Category 3: Node.js / npm Errors + +| Code | Meaning | Recovery | +|------|---------|----------| +| 243 | Node.js: Out of memory (heap out of memory) | Increase container RAM, reduce workload | +| 245 | Node.js: Invalid command-line option | Check node/npm arguments | +| 246 | Node.js: Internal JavaScript Parse Error | Update Node.js version | +| 247 | Node.js: Fatal internal error | Check Node.js installation integrity | +| 248 | Node.js: Invalid C++ addon / N-API failure | Rebuild native modules | +| 249 | Node.js: Inspector error | Disable debugger, retry | +| 254 | npm/pnpm/yarn: Unknown fatal error | Check package.json, clear cache | + +### Category 4: Python Errors + +| Code | Meaning | Recovery | +|------|---------|----------| +| 210 | Python: Virtualenv / uv environment missing | Recreate virtual environment | +| 211 | Python: Dependency resolution failed | Check package versions, fix conflicts | +| 212 | Python: Installation aborted (EXTERNALLY-MANAGED) | Use venv or remove marker file | + +### Category 5: Database Errors + +#### PostgreSQL + +| Code | Meaning | Recovery | +|------|---------|----------| +| 231 | Connection failed (server not running) | Start PostgreSQL service | +| 232 | Authentication failed (bad user/password) | Verify credentials | +| 233 | Database does not exist | Create database: `createdb dbname` | +| 234 | Fatal error in query / syntax error | Fix SQL syntax | + +#### MySQL / MariaDB + +| Code | Meaning | Recovery | +|------|---------|----------| +| 241 | Connection failed (server not running) | Start MySQL/MariaDB service | +| 242 | Authentication failed (bad user/password) | Reset password, verify credentials | +| 243 | Database does not exist | Create database: `CREATE DATABASE dbname;` | +| 244 | Fatal error in query / syntax error | Fix SQL syntax | + +#### MongoDB + +| Code | Meaning | Recovery | +|------|---------|----------| +| 251 | Connection failed (server not running) | Start MongoDB daemon | +| 252 | Authentication failed (bad user/password) | Verify credentials, reset if needed | +| 253 | Database not found | Create database in MongoDB shell | +| 254 | Fatal query error | Check query syntax | + +### Category 6: Proxmox Custom Codes + +| Code | Meaning | Recovery | +|------|---------|----------| +| 200 | Failed to create lock file | Check /tmp permissions | +| 203 | Missing CTID variable | CTID must be provided to script | +| 204 | Missing PCT_OSTYPE variable | OS type not detected | +| 205 | Invalid CTID (<100) | Container ID must be >= 100 | +| 206 | CTID already in use | Check `pct list`, remove conflicting container | +| 207 | Password contains special characters | Use alphanumeric only for passwords | +| 208 | Invalid configuration format | Check DNS/MAC/Network format | +| 209 | Container creation failed | Check pct create output for details | +| 210 | Cluster not quorate | Ensure cluster nodes are online | +| 211 | Timeout waiting for template lock | Wait for concurrent downloads to finish | +| 214 | Not enough storage space | Free up disk space or expand storage | +| 215 | Container created but not listed | Check /etc/pve/lxc/ for config files | +| 216 | RootFS entry missing in config | Incomplete container creation | +| 217 | Storage does not support rootdir | Use compatible storage backend | +| 218 | Template corrupted or incomplete | Re-download template | +| 220 | Unable to resolve template path | Verify template availability | +| 221 | Template not readable | Fix file permissions | +| 222 | Template download failed (3 attempts) | Check network/storage | +| 223 | Template not available after download | Storage sync issue | +| 225 | No template for OS/Version | Run `pveam available` to see options | +| 231 | LXC stack upgrade/retry failed | Update pve-container package | + +--- + +## Telemetry Functions + +### `explain_exit_code()` + +**Purpose**: Maps numeric exit codes to human-readable error descriptions. Shared between api.func and error_handler.func for consistency. + +**Signature**: +```bash +explain_exit_code() +``` + +**Parameters**: +- `$1` - Numeric exit code (0-255) + +**Returns**: Human-readable description string + +**Supported Codes**: +- 1-2, 126-128, 130, 137, 139, 143 (Shell) +- 100-101, 255 (Package managers) +- 210-212 (Python) +- 231-234 (PostgreSQL) +- 241-244 (MySQL/MariaDB) +- 243-249, 254 (Node.js/npm) +- 251-254 (MongoDB) +- 200-231 (Proxmox custom) + +**Default**: Returns "Unknown error" for unmapped codes + +**Usage Examples**: + +```bash +# Example 1: Common error +explain_exit_code 127 +# Output: "Command not found" + +# Example 2: Database error +explain_exit_code 241 +# Output: "MySQL/MariaDB: Connection failed (server not running / wrong socket)" + +# Example 3: Custom Proxmox error +explain_exit_code 206 +# Output: "Custom: CTID already in use (check 'pct list' and /etc/pve/lxc/)" + +# Example 4: Unknown code +explain_exit_code 999 +# Output: "Unknown error" +``` + +--- + +### `post_to_api()` + +**Purpose**: Sends LXC container creation statistics to Community-Scripts telemetry API. + +**Signature**: +```bash +post_to_api() +``` + +**Parameters**: None (uses global environment variables) + +**Returns**: No explicit return value (curl result stored in RESPONSE if diagnostics enabled) + +**Requirements** (Silent fail if not met): +- `curl` command available +- `DIAGNOSTICS="yes"` +- `RANDOM_UUID` is set +- Executed on Proxmox host (has access to `pveversion`) + +**Environment Variables Used**: +- `CT_TYPE` - Container type (privileged=1, unprivileged=0) +- `DISK_SIZE` - Allocated disk in GB +- `CORE_COUNT` - CPU core count +- `RAM_SIZE` - RAM allocated in MB +- `var_os` - Operating system name +- `var_version` - OS version +- `NSAPP` - Normalized application name +- `METHOD` - Installation method (default, template, etc.) +- `DIAGNOSTICS` - Enable telemetry (yes/no) +- `RANDOM_UUID` - Session UUID for tracking + +**API Endpoint**: `http://api.community-scripts.org/dev/upload` + +**Payload Structure**: +```json +{ + "ct_type": 1, // Privileged (1) or Unprivileged (0) + "type": "lxc", // Always "lxc" for containers + "disk_size": 8, // GB + "core_count": 2, // CPU cores + "ram_size": 2048, // MB + "os_type": "debian", // OS name + "os_version": "12", // OS version + "nsapp": "myapp", // Application name + "method": "default", // Setup method + "pve_version": "8.2.2", // Proxmox VE version + "status": "installing", // Current status + "random_id": "550e8400-e29b" // Session UUID (anonymous) +} +``` + +**Usage Examples**: + +```bash +# Example 1: Successful API post +CT_TYPE=1 +DISK_SIZE=20 +CORE_COUNT=4 +RAM_SIZE=4096 +var_os="ubuntu" +var_version="22.04" +NSAPP="jellyfin" +METHOD="default" +DIAGNOSTICS="yes" +RANDOM_UUID="550e8400-e29b-41d4-a716-446655440000" + +post_to_api +# Result: Statistics sent to API (silently, no output) + +# Example 2: Diagnostics disabled (opt-out) +DIAGNOSTICS="no" +post_to_api +# Result: Function returns immediately, no API call + +# Example 3: Missing curl +DIAGNOSTICS="yes" +# curl not available in PATH +post_to_api +# Result: Function returns silently (curl requirement not met) +``` + +--- + +### `post_to_api_vm()` + +**Purpose**: Sends VM creation statistics to Community-Scripts API (similar to post_to_api but for virtual machines). + +**Signature**: +```bash +post_to_api_vm() +``` + +**Parameters**: None (uses global environment variables) + +**Returns**: No explicit return value + +**Requirements**: Same as `post_to_api()` + +**Environment Variables Used**: +- `VMID` - Virtual machine ID +- `VM_TYPE` - VM type (kvm, etc.) +- `VM_CORES` - CPU core count +- `VM_RAM` - RAM in MB +- `VM_DISK` - Disk in GB +- `VM_OS` - Operating system +- `VM_VERSION` - OS version +- `VM_APP` - Application name +- `DIAGNOSTICS` - Enable telemetry +- `RANDOM_UUID` - Session UUID + +**Payload Structure** (similar to containers but for VMs): +```json +{ + "vm_id": 100, + "type": "qemu", + "vm_cores": 4, + "vm_ram": 4096, + "vm_disk": 20, + "vm_os": "ubuntu", + "vm_version": "22.04", + "vm_app": "jellyfin", + "pve_version": "8.2.2", + "status": "installing", + "random_id": "550e8400-e29b" +} +``` + +--- + +### `post_update_to_api()` + +**Purpose**: Reports installation completion status (success/failure) for container or VM. + +**Signature**: +```bash +post_update_to_api() +``` + +**Parameters**: None (uses global environment variables) + +**Returns**: No explicit return value + +**Requirements**: Same as `post_to_api()` + +**Environment Variables Used**: +- `RANDOM_UUID` - Session UUID (must match initial post_to_api call) +- `DIAGNOSTICS` - Enable telemetry +- Installation status parameters + +**Payload Structure**: +```json +{ + "status": "completed", // "completed" or "failed" + "random_id": "550e8400-e29b", // Session UUID + "exit_code": 0, // 0 for success, error code for failure + "error_explanation": "" // Error description if failed +} +``` + +--- + +## API Payload Structure + +### Container Creation Payload + +```json +{ + "ct_type": 1, // 1=Privileged, 0=Unprivileged + "type": "lxc", // Always "lxc" + "disk_size": 20, // GB + "core_count": 4, // CPU cores + "ram_size": 4096, // MB + "os_type": "debian", // Distribution name + "os_version": "12", // Version number + "nsapp": "jellyfin", // Application name + "method": "default", // Setup method + "pve_version": "8.2.2", // Proxmox VE version + "status": "installing", // Current phase + "random_id": "550e8400" // Unique session ID +} +``` + +### VM Creation Payload + +```json +{ + "vm_id": 100, + "type": "qemu", + "vm_cores": 4, + "vm_ram": 4096, + "vm_disk": 20, + "vm_os": "ubuntu", + "vm_version": "22.04", + "vm_app": "jellyfin", + "pve_version": "8.2.2", + "status": "installing", + "random_id": "550e8400" +} +``` + +### Update/Completion Payload + +```json +{ + "status": "completed", + "random_id": "550e8400", + "exit_code": 0, + "error_explanation": "" +} +``` + +--- + +## Privacy & Opt-Out + +### Privacy Policy + +Community-Scripts telemetry is designed to be **privacy-respecting**: + +- βœ… **Anonymous**: No personal data collected +- βœ… **Session-based**: UUID allows correlation without identification +- βœ… **Aggregated**: Only statistics are stored, never raw logs +- βœ… **Opt-out capable**: Single environment variable disables all telemetry +- βœ… **No tracking**: UUID cannot be linked to user identity +- βœ… **No credentials**: Passwords, SSH keys never transmitted + +### Opt-Out Methods + +**Method 1: Environment Variable (Single Script)** + +```bash +DIAGNOSTICS="no" bash ct/myapp.sh +``` + +**Method 2: Script Header (Persistent)** + +```bash +#!/bin/bash +export DIAGNOSTICS="no" +# Rest of script continues without telemetry +``` + +**Method 3: System-wide Configuration** + +```bash +# In /etc/environment or ~/.bashrc +export DIAGNOSTICS="no" +``` + +### What Data Is Collected + +| Data | Why | Shared? | +|------|-----|---------| +| Container/VM specs (cores, RAM, disk) | Understand deployment patterns | Yes, aggregated | +| OS type/version | Track popular distributions | Yes, aggregated | +| Application name | Understand popular apps | Yes, aggregated | +| Method (standard vs. custom) | Measure feature usage | Yes, aggregated | +| Success/failure status | Identify issues | Yes, aggregated | +| Exit codes | Debug failures | Yes, anonymized | + +### What Data Is NOT Collected + +- ❌ Container/VM hostnames +- ❌ IP addresses +- ❌ User credentials +- ❌ SSH keys or secrets +- ❌ Application data +- ❌ System logs +- ❌ Any personal information + +--- + +## Error Mapping + +### Mapping Strategy + +Exit codes are categorized by source: + +``` +Exit Code Range | Source | Handling +0 | Success | Not reported to API +1-2 | Shell/Script | Generic error +100-101, 255 | Package managers | APT/DPKG specific +126-128 | Command execution | Permission/not found +130, 143 | Signals | User interrupt/termination +137, 139 | Kernel | OOM/segfault +200-231 | Proxmox custom | Container creation issues +210-212 | Python | Python environment issues +231-234 | PostgreSQL | Database connection issues +241-244 | MySQL/MariaDB | Database connection issues +243-249, 254 | Node.js/npm | Runtime errors +251-254 | MongoDB | Database connection issues +``` + +### Custom Exit Code Usage + +Scripts can define custom exit codes: + +```bash +# Example: Custom validation failure +if [[ "$CTID" -lt 100 ]]; then + echo "Container ID must be >= 100" + exit 205 # Custom Proxmox code +fi +``` + +--- + +## Best Practices + +### 1. **Always Initialize RANDOM_UUID** + +```bash +# Generate unique session ID for tracking +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" + +# Use first 8 chars for short session ID (logs) +SESSION_ID="${RANDOM_UUID:0:8}" +BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" +``` + +### 2. **Call post_to_api Early** + +```bash +# Call post_to_api right after container creation starts +# This tracks attempt, even if installation fails + +variables() { + RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" + # ... other variables ... +} + +# Later, in main script: +post_to_api # Report container creation started +# ... perform installation ... +post_update_to_api # Report completion +``` + +### 3. **Handle Graceful Failures** + +```bash +# Wrap API calls to handle network issues +if command -v curl &>/dev/null; then + post_to_api || true # Don't fail if API unavailable +else + msg_warn "curl not available, telemetry skipped" +fi +``` + +### 4. **Respect User Opt-Out** + +```bash +# Check DIAGNOSTICS early and skip all API calls if disabled +if [[ "${DIAGNOSTICS}" != "yes" ]]; then + msg_info "Anonymous diagnostics disabled" + return 0 # Skip telemetry +fi +``` + +### 5. **Maintain Session Consistency** + +```bash +# Use same RANDOM_UUID throughout lifecycle +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" + +post_to_api # Initial report +# ... installation ... +post_update_to_api # Final report (same UUID links them) +``` + +--- + +## API Integration + +### Connecting to API + +The API endpoint is: + +``` +http://api.community-scripts.org/dev/upload +``` + +### API Response Handling + +```bash +# Capture HTTP response code +RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" \ + -H "Content-Type: application/json" \ + -d "$JSON_PAYLOAD") || true + +# Extract status code (last 3 digits) +HTTP_CODE="${RESPONSE: -3}" + +if [[ "$HTTP_CODE" == "200" ]]; then + msg_ok "Telemetry submitted successfully" +elif [[ "$HTTP_CODE" == "429" ]]; then + msg_warn "API rate limited, skipping telemetry" +else + msg_info "Telemetry API unreachable (this is OK)" +fi +``` + +### Network Resilience + +API calls are **best-effort** and never block installation: + +```bash +# Telemetry should never cause container creation to fail +if post_to_api 2>/dev/null; then + msg_info "Diagnostics transmitted" +fi +# If API unavailable, continue anyway +``` + +--- + +## Contributing + +### Adding New Exit Codes + +1. Document in the appropriate category section +2. Update `explain_exit_code()` in both api.func and error_handler.func +3. Add recovery suggestions +4. Test mapping with scripts that use the new code + +### Testing API Integration + +```bash +# Test with mock curl (local testing) +DIAGNOSTICS="yes" +RANDOM_UUID="test-uuid-12345678" +curl -X POST http://localhost:8000/dev/upload \ + -H "Content-Type: application/json" \ + -d '{"test": "payload"}' + +# Verify payload structure +post_to_api 2>&1 | head -20 +``` + +### Telemetry Reporting Improvements + +Suggestions for improvement: + +1. Installation duration tracking +2. Package version compatibility data +3. Feature usage analytics +4. Performance metrics +5. Custom error codes + +--- + +## Notes + +- API calls are **silent by default** and never display sensitive information +- Telemetry can be **completely disabled** via `DIAGNOSTICS="no"` +- **RANDOM_UUID must be generated** before calling any post functions +- Exit code mappings are **shared** between api.func and error_handler.func for consistency +- API is **optional** - containers work perfectly without telemetry +