with one click
swizzin-scripts-patterns
// Coding patterns extracted from swizzin-scripts repository for Swizzin installer scripts
// Coding patterns extracted from swizzin-scripts repository for Swizzin installer scripts
| name | swizzin-scripts-patterns |
| description | Coding patterns extracted from swizzin-scripts repository for Swizzin installer scripts |
| version | 1.0.0 |
| source | local-git-analysis |
| analyzed_commits | 187 |
| date_range | 2025-12-19 to 2026-01-30 |
This skill teaches Claude the coding patterns, conventions, and workflows used in this repository of Swizzin installer scripts.
This project uses imperative mood commits with action prefixes:
| Prefix | Usage | Frequency |
|---|---|---|
Fix | Bug fixes and corrections | 45 commits (24%) |
Add | New features, files, or capabilities | 57 commits (30%) |
Update | Modifications to existing features | 11 commits (6%) |
Remove | Deletions and cleanup | 5 commits (3%) |
Examples of good commit messages:
Fix LibreTranslate container permission errorAdd subdomain support to Lingarr installerUpdate docs for subgen Python 3.11 and GPU auto-detectionFix Lingarr: host networking, UrlBase support, remove broken auth rulesAnti-patterns to avoid:
swizzin-scripts/
āāā *.sh # Main installer scripts
āāā templates/ # Starter templates for new scripts
ā āāā template-binary.sh # Single binary apps ā /usr/bin
ā āāā template-python.sh # Python/uv apps ā /opt
ā āāā template-docker.sh # Docker Compose apps ā /opt
ā āāā template-subdomain.sh # Extended installers with subdomain
ā āāā template-multiinstance.sh # Multi-instance managers
āāā panel_helpers.sh # Shared panel integration utilities
āāā backup/ # BorgBackup system
āāā watchdog/ # Service health monitoring
āāā configs/ # Example configuration files
āāā docs/plans/ # Design documents (YYYY-MM-DD-*-design.md)
āāā CLAUDE.md # AI assistant context
āāā README.md # User documentation
| Type | Pattern | Binary Location | Examples |
|---|---|---|---|
| Single binary | template-binary.sh | /usr/bin/<app> | notifiarr, decypharr, cleanuparr |
| Python/uv | template-python.sh | /opt/<app>/ | byparr, huntarr, subgen |
| Docker | template-docker.sh | /opt/<app>/ | lingarr, libretranslate |
| Extended | template-subdomain.sh | varies | plex, emby, jellyfin, seerr |
| Multi-instance | template-multiinstance.sh | varies | sonarr, radarr, bazarr |
All internal functions use underscore prefix with pattern _<action>_<appname>():
_install_notifiarr() # Main installation logic
_systemd_notifiarr() # Create systemd service
_nginx_notifiarr() # Create nginx config
_remove_notifiarr() # Removal logic
_load_panel_helper() # Load panel helper (standard)
Other common function names:
_get_domain() / _prompt_domain() - Domain management_get_install_state() - Detect subfolder vs subdomain_create_subdomain_vhost() - Nginx subdomain config_validate_instance_name() - Input validation_ensure_base_installed() - Prerequisite checksEvery installer script follows this exact sequence:
#!/bin/bash
# <appname> installer
# STiXzoOR 2025
# Usage: bash <appname>.sh [--remove [--force]|--register-panel]
# 1. Source Swizzin utilities
. /etc/swizzin/sources/globals.sh
#shellcheck source=sources/functions/utils
. /etc/swizzin/sources/functions/utils
# 2. Panel helper (download and cache)
PANEL_HELPER_LOCAL="/opt/swizzin-extras/panel_helpers.sh"
PANEL_HELPER_URL="https://raw.githubusercontent.com/STiXzoOR/swizzin-scripts/main/panel_helpers.sh"
_load_panel_helper() { ... }
# 3. Logging setup
export log=/root/logs/swizzin.log
touch "$log"
# 4. App configuration variables
app_name="myapp"
app_port=$(port 10000 12000)
app_dir="/usr/bin" # or /opt/myapp
app_configdir="/home/${user}/.config/Myapp"
app_icon_url="https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/myapp.png"
# 5. Owner resolution (from swizdb or master user)
if ! app_owner="$(swizdb get "${app_name}/owner" 2>/dev/null)"; then
app_owner="$(_get_master_username)"
fi
user="${app_owner}"
# 6. Core functions
_install_myapp() { ... }
_systemd_myapp() { ... }
_nginx_myapp() { ... }
_remove_myapp() { ... }
# 7. Main logic (argument parsing)
case "$1" in
--remove) _remove_myapp "$@" ;;
--register-panel) _load_panel_helper && panel_register_app ... ;;
*) _install_myapp && _systemd_myapp && _nginx_myapp && ... ;;
esac
Always quote variables and use braces for clarity:
# CORRECT
touch "$log"
chown "${user}:${user}" "$config_dir"
mkdir -p "${app_dir}/${app_name}"
if [[ -f "$file" ]]; then
# INCORRECT
touch $log
chown $user:$user $config_dir
mkdir -p $app_dir/$app_name
| Function | Purpose | Example |
|---|---|---|
port <start> <end> | Allocate free port | app_port=$(port 10000 12000) |
apt_install <pkgs> | Install apt packages | apt_install curl jq |
swizdb get/set | Persistent storage | swizdb set "myapp/port" "$port" |
_get_master_username | Primary Swizzin user | user=$(_get_master_username) |
ask "question?" Y/N | Interactive prompt | if ask "Continue?" Y; then |
echo_progress_start/done | Progress indicators | echo_progress_start "Installing" |
echo_error/info/query | Status messages | echo_error "Failed to install" |
_load_panel_helper
panel_register_app \
"$app_name" \
"$app_port" \
"$app_lockname" \
"$app_baseurl" \
"$app_icon_name" \
"$app_icon_url"
/install/.<appname>.lock# Check if already installed
if [[ -f "/install/.${app_lockname}.lock" ]]; then
echo_error "${app_name} is already installed"
exit 1
fi
# Create lock file at end
touch "/install/.${app_lockname}.lock"
location /${app_name}/ {
proxy_pass http://127.0.0.1:${app_port}/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
/etc/nginx/sites-available/<appname>/etc/nginx/sites-enabled/<appname>box install letsencrypt[Unit]
Description=${app_pretty}
After=network-online.target
[Service]
User=${user}
Group=${user}
Type=simple
ExecStart=${app_binary} --config ${app_configdir}/config.yml
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
For Docker apps:
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/docker compose -f /opt/${app_name}/docker-compose.yml up -d
ExecStop=/usr/bin/docker compose -f /opt/${app_name}/docker-compose.yml down
Before implementing complex features, create a design document:
docs/plans/YYYY-MM-DD-<feature>-design.md> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-planstemplates/template-binary.sh to <appname>.shmyapp ā yourapp, Myapp ā Yourapp_install_<app>()swizzin-app-info APP_CONFIGStemplates/template-subdomain.sh_get_domain() / _prompt_domain()_create_subdomain_vhost()_revert_subdomain()--subdomain and --subdomain --revert flagsFix <description> messagenginx -tScripts support automation via environment variables:
# Skip interactive prompts
<APP>_DOMAIN="app.example.com"
<APP>_LE_HOSTNAME="app.example.com"
<APP>_LE_INTERACTIVE="yes" # for CloudFlare DNS
<APP>_OWNER="username"
DN_API_KEY="xxxxx" # for notifiarr
Based on git history, these files change together:
| Primary File | Co-changed Files |
|---|---|
lingarr.sh | CLAUDE.md, README.md, templates/template-docker.sh |
zurg.sh | bootstrap/lib/apps.sh, decypharr.sh |
*.sh (new) | CLAUDE.md, README.md, swizzin-app-info |
organizr.sh | Related subdomain scripts |
_load_panel_helper()port 10000 12000[ ] for complex tests - Prefer [[ ]]-i for rebase/add[HINT] Download the complete skill directory including SKILL.md and all related files