Awesome-omni-skill swizzin-scripts-patterns
Coding patterns extracted from swizzin-scripts repository for Swizzin installer scripts
install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/swizzin-scripts-patterns" ~/.claude/skills/diegosouzapw-awesome-omni-skill-swizzin-scripts-patterns && rm -rf "$T"
manifest:
skills/development/swizzin-scripts-patterns/SKILL.mdsource content
Swizzin Scripts Patterns
This skill teaches Claude the coding patterns, conventions, and workflows used in this repository of Swizzin installer scripts.
Commit Conventions
This project uses imperative mood commits with action prefixes:
| Prefix | Usage | Frequency |
|---|---|---|
| Bug fixes and corrections | 45 commits (24%) |
| New features, files, or capabilities | 57 commits (30%) |
| Modifications to existing features | 11 commits (6%) |
| 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 rules
Anti-patterns to avoid:
- Past tense: "Fixed" → use "Fix"
- Vague descriptions: "Updates" → be specific about what changed
- No prefix: Always start with an action word
Code Architecture
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
Script Categories
| Type | Pattern | Binary Location | Examples |
|---|---|---|---|
| Single binary | | | notifiarr, decypharr, cleanuparr |
| Python/uv | | | byparr, huntarr, subgen |
| Docker | | | lingarr, libretranslate |
| Extended | | varies | plex, emby, jellyfin, seerr |
| Multi-instance | | varies | sonarr, radarr, bazarr |
Function Naming Convention
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()
- Domain management_prompt_domain()
- Detect subfolder vs subdomain_get_install_state()
- Nginx subdomain config_create_subdomain_vhost()
- Input validation_validate_instance_name()
- Prerequisite checks_ensure_base_installed()
Script Structure Pattern
Every 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
Variable Quoting Standards
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
Swizzin API Functions
| Function | Purpose | Example |
|---|---|---|
| Allocate free port | |
| Install apt packages | |
| Persistent storage | |
| Primary Swizzin user | |
| Interactive prompt | |
| Progress indicators | |
| Status messages | |
Panel Registration Pattern
_load_panel_helper panel_register_app \ "$app_name" \ "$app_port" \ "$app_lockname" \ "$app_baseurl" \ "$app_icon_name" \ "$app_icon_url"
Lock File Convention
- Location:
/install/.<appname>.lock - Created at end of successful install
- Checked before install to prevent duplicate installs
- Removed during uninstall
# 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"
Nginx Configuration Patterns
Subfolder Mode (location block)
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; }
Subdomain Mode (dedicated vhost)
- File:
/etc/nginx/sites-available/<appname> - Symlink:
/etc/nginx/sites-enabled/<appname> - Let's Encrypt via
box install letsencrypt
Systemd Service Pattern
[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
Design Document Pattern
Before implementing complex features, create a design document:
- Location:
docs/plans/YYYY-MM-DD-<feature>-design.md - Include: Goal, Architecture, Tech Stack, Design Decisions table, File Layout, Tasks
- Reference:
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans
Common Workflows
Adding a New Binary App
- Copy
totemplates/template-binary.sh<appname>.sh - Search/replace
→myapp
,yourapp
→MyappYourapp - Customize:
- Architecture mapping in
_install_<app>() - Config file format
- Systemd service options
- Nginx location config
- Architecture mapping in
- Add entry to
APP_CONFIGSswizzin-app-info - Update backup/restore scripts
- Update README.md and CLAUDE.md
Adding Subdomain Support
- Copy
templates/template-subdomain.sh - Implement:
/_get_domain()_prompt_domain()_create_subdomain_vhost()_revert_subdomain()
- Handle Organizr SSO exclusion if needed
- Support
and--subdomain
flags--subdomain --revert
Fixing a Bug
- Identify root cause in existing script
- Make minimal change to fix
- Test on Swizzin system
- Commit with
messageFix <description>
Testing Approach
- No automated tests; manual testing on Swizzin systems
- Test both install and remove paths
- Verify nginx config with
nginx -t - Check systemd status after install
- Confirm panel registration
Environment Variable Bypass
Scripts 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
Files Modified Together
Based on git history, these files change together:
| Primary File | Co-changed Files |
|---|---|
| , , |
| , |
(new) | , , |
| Related subdomain scripts |
Anti-Patterns to Avoid
- Don't skip panel helper - Always include
_load_panel_helper() - Don't hardcode ports - Use
port 10000 12000 - Don't use
for complex tests - Prefer[ ][[ ]] - Don't forget lock files - Both create and check
- Don't commit without testing remove - Always verify cleanup
- Don't use interactive flags with git - No
for rebase/add-i - Don't echo to communicate - Use proper Swizzin functions