ProxmoxVED/docs/alpine-tools.func.md
2025-12-01 10:40:14 +01:00

589 lines
11 KiB
Markdown

# Alpine-Tools.func Wiki
Alpine Linux-specific tool setup and package management module providing helper functions optimized for Alpine's apk package manager and minimal container environment.
---
## 📋 Table of Contents
- [Overview](#overview)
- [Helper Functions](#helper-functions)
- [GitHub Release Functions](#github-release-functions)
- [Tool Installation Patterns](#tool-installation-patterns)
- [Package Management](#package-management)
- [Best Practices](#best-practices)
- [Debugging](#debugging)
- [Contributing](#contributing)
---
## Overview
Alpine-tools.func provides **Alpine Linux-specific utilities**:
- ✅ Alpine apk package manager wrapper
- ✅ GitHub release version checking and installation
- ✅ Tool caching and version tracking
- ✅ Progress reporting with pv (pipe viewer)
- ✅ Network resolution helpers for Alpine
- ✅ PATH persistence across sessions
- ✅ Retry logic for failed downloads
- ✅ Minimal dependencies philosophy (Alpine ~5MB containers)
### Key Differences from Debian/Ubuntu
| Feature | Alpine | Debian/Ubuntu |
|---------|--------|---------------|
| Package Manager | apk | apt-get, dpkg |
| Shell | ash (dash variant) | bash |
| Init System | OpenRC | systemd |
| Size | ~5MB base | ~100MB+ base |
| Libc | musl | glibc |
| Find getent | Not installed | Installed |
### Integration Pattern
```bash
#!/bin/sh # Alpine uses ash, not bash
source <(curl -fsSL .../core.func)
source <(curl -fsSL .../alpine-tools.func)
load_functions
# Now Alpine-specific tool functions available
need_tool curl jq # Install if missing
check_for_gh_release "myapp" "owner/repo"
```
---
## Helper Functions
### `lower()`
**Purpose**: Converts string to lowercase (portable ash function).
**Signature**:
```bash
lower()
```
**Parameters**:
- `$1` - String to convert
**Returns**: Lowercase string on stdout
**Behavior**:
```bash
# Alpine's tr works with character classes
printf '%s' "$1" | tr '[:upper:]' '[:lower:]'
```
**Usage Examples**:
```bash
# Example 1: App name normalization
result=$(lower "MyApp")
echo "$result" # Output: myapp
# Example 2: In variable assignment
app_dir=$(lower "$APPLICATION")
mkdir -p /opt/$app_dir
```
---
### `has()`
**Purpose**: Checks if command is available in PATH.
**Signature**:
```bash
has()
```
**Parameters**:
- `$1` - Command name
**Returns**: 0 if available, 1 if not
**Implementation**:
```bash
has() {
command -v "$1" >/dev/null 2>&1
}
```
**Usage Examples**:
```bash
# Example 1: Check availability
if has jq; then
echo "jq is installed"
else
echo "jq is not installed"
fi
# Example 2: In conditionals
has docker && docker ps || echo "Docker not installed"
```
---
### `need_tool()`
**Purpose**: Ensures specified tools are installed, installs missing ones via apk.
**Signature**:
```bash
need_tool()
```
**Parameters**:
- `$@` - Tool names (space-separated)
**Returns**: 0 on success, 1 if installation failed
**Behavior**:
```bash
# Checks each tool
# If any missing: runs apk add for all
# Displays message before and after
```
**Error Handling**:
- Returns 1 if apk add fails
- Shows which tools failed
- Suggests checking package names
**Usage Examples**:
```bash
# Example 1: Ensure common tools available
need_tool curl jq unzip git
# Installs any missing packages
# Example 2: Optional tool check
if need_tool myapp-cli; then
myapp-cli --version
else
echo "myapp-cli not available in apk"
fi
# Example 3: With error handling
need_tool docker || {
echo "Failed to install docker"
exit 1
}
```
---
### `net_resolves()`
**Purpose**: Checks if hostname resolves and responds (Alpine-friendly DNS test).
**Signature**:
```bash
net_resolves()
```
**Parameters**:
- `$1` - Hostname to test
**Returns**: 0 if resolves and responds, 1 if fails
**Behavior**:
```bash
# Alpine doesn't have getent by default
# Falls back to nslookup if ping fails
# Returns success if either works
ping -c1 -W1 "$host" >/dev/null 2>&1 || nslookup "$host" >/dev/null 2>&1
```
**Usage Examples**:
```bash
# Example 1: Test GitHub connectivity
if net_resolves api.github.com; then
echo "Can reach GitHub API"
else
echo "GitHub API unreachable"
fi
# Example 2: In download function
net_resolves download.example.com || {
echo "Download server not reachable"
exit 1
}
```
---
### `ensure_usr_local_bin_persist()`
**Purpose**: Ensures `/usr/local/bin` is in PATH across all shell sessions.
**Signature**:
```bash
ensure_usr_local_bin_persist()
```
**Parameters**: None
**Returns**: No explicit return value (modifies system)
**Behavior**:
```bash
# Creates /etc/profile.d/10-localbin.sh
# Script adds /usr/local/bin to PATH if not already present
# Runs on every shell startup
# Alpine uses /etc/profile for login shells
# profile.d scripts sourced automatically
```
**Implementation**:
```bash
PROFILE_FILE="/etc/profile.d/10-localbin.sh"
if [ ! -f "$PROFILE_FILE" ]; then
echo 'case ":$PATH:" in *:/usr/local/bin:*) ;; *) export PATH="/usr/local/bin:$PATH";; esac' > "$PROFILE_FILE"
chmod +x "$PROFILE_FILE"
fi
```
**Usage Examples**:
```bash
# Example 1: Make sure local tools available
ensure_usr_local_bin_persist
# Now /usr/local/bin binaries always in PATH
# Example 2: After installing custom tool
cp ./my-tool /usr/local/bin/
ensure_usr_local_bin_persist
# Tool immediately accessible in PATH
```
---
### `download_with_progress()`
**Purpose**: Downloads file with progress bar (if pv available) or simple # progress.
**Signature**:
```bash
download_with_progress()
```
**Parameters**:
- `$1` - URL to download
- `$2` - Destination file path
**Returns**: 0 on success, 1 on failure
**Behavior**:
```bash
# Attempts to get content-length header
# If available: pipes through pv for progress bar
# If not: uses curl's built-in # progress
# Shows errors clearly
```
**Requirements**:
- `curl` - For downloading
- `pv` - Optional, for progress bar
- Destination directory must exist
**Usage Examples**:
```bash
# Example 1: Simple download
download_with_progress "https://example.com/file.tar.gz" "/tmp/file.tar.gz"
# Shows progress bar if pv available
# Example 2: With error handling
if download_with_progress "$URL" "$DEST"; then
echo "Downloaded successfully"
tar -xzf "$DEST"
else
echo "Download failed"
exit 1
fi
```
---
## GitHub Release Functions
### `check_for_gh_release()`
**Purpose**: Checks GitHub releases for available updates and compares with currently installed version.
**Signature**:
```bash
check_for_gh_release()
```
**Parameters**:
- `$1` - Application name (e.g., "nodejs")
- `$2` - GitHub repository (e.g., "nodejs/node")
- `$3` - Pinned version (optional, e.g., "20.0.0")
**Returns**: 0 if update needed, 1 if current or pinned
**Environment Variables Set**:
- `CHECK_UPDATE_RELEASE` - Latest available version (without v prefix)
**Behavior**:
```bash
# 1. Check network to api.github.com
# 2. Fetch latest release tag via GitHub API
# 3. Compare with installed version (stored in ~/.appname)
# 4. Show appropriate message:
# - "app pinned to vX.X.X (no update)"
# - "app pinned vX.X.X (upstream vY.Y.Y) → update/downgrade"
# - "Update available: vA.A.A → vB.B.B"
# - "Already up to date"
```
**File Storage**:
```bash
~/.${app_lc} # File contains current version string
# Example: ~/.nodejs contains "20.10.0"
```
**Usage Examples**:
```bash
# Example 1: Check for update
check_for_gh_release "nodejs" "nodejs/node"
# Output: "Update available: v18.0.0 → v20.10.0"
# Sets: CHECK_UPDATE_RELEASE="20.10.0"
# Example 2: Pinned version (no update)
check_for_gh_release "nodejs" "nodejs/node" "20.0.0"
# Output: "app pinned to v20.0.0 (no update)"
# Returns 1 (no update available)
# Example 3: With error handling
if check_for_gh_release "myapp" "owner/myapp"; then
echo "Update available: $CHECK_UPDATE_RELEASE"
download_and_install
fi
```
---
## Tool Installation Patterns
### Pattern 1: Simple Package Installation
```bash
#!/bin/sh
need_tool curl jq # Ensure tools available
# Continue with script
```
### Pattern 2: GitHub Release Installation
```bash
#!/bin/sh
source <(curl -fsSL .../alpine-tools.func)
load_functions
# Check for updates
check_for_gh_release "myapp" "owner/myapp"
# Download from GitHub releases
RELEASE="$CHECK_UPDATE_RELEASE"
URL="https://github.com/owner/myapp/releases/download/v${RELEASE}/myapp-alpine.tar.gz"
download_with_progress "$URL" "/tmp/myapp-${RELEASE}.tar.gz"
tar -xzf "/tmp/myapp-${RELEASE}.tar.gz" -C /usr/local/bin/
```
### Pattern 3: Version Pinning
```bash
#!/bin/sh
# For specific use case, pin to known good version
check_for_gh_release "nodejs" "nodejs/node" "20.10.0"
# Will use 20.10.0 even if 21.0.0 available
```
---
## Package Management
### Alpine Package Naming
Alpine packages often have different names than Debian:
| Tool | Alpine | Debian |
|------|--------|--------|
| curl | curl | curl |
| Git | git | git |
| Docker | docker | docker.io |
| PostgreSQL | postgresql-client | postgresql-client |
| Build tools | build-base | build-essential |
| Development headers | -dev packages | -dev packages |
### Finding Alpine Packages
```bash
# Search for package
apk search myapp
# Show package info
apk info -d myapp
# List available versions
apk search myapp --all
```
### Installing Alpine Packages
```bash
# Basic install (not cached)
apk add curl git
# Install with --no-cache (for containers)
apk add --no-cache curl git
# Force broken packages (last resort)
apk add --no-cache --force-broken-world util-linux
```
---
## Best Practices
### 1. **Use `--no-cache` in Containers**
```bash
# Good: Saves space in container
apk add --no-cache curl git
# Avoid: Wastes space
apk update && apk add curl git
```
### 2. **Check Tools Before Using**
```bash
# Good: Graceful error
if ! has jq; then
need_tool jq || exit 1
fi
# Using jq safely
jq . < input.json
```
### 3. **Use need_tool() for Multiple**
```bash
# Good: Install all at once
need_tool curl jq git unzip
# Less efficient: Individual checks
has curl || apk add curl
has jq || apk add jq
```
### 4. **Ensure Persistence**
```bash
# For custom tools in /usr/local/bin
ensure_usr_local_bin_persist
# Now available in all future shells
/usr/local/bin/my-custom-tool
```
### 5. **Handle Network Failures**
```bash
# Alpine often in isolated environments
if ! net_resolves api.github.com; then
echo "GitHub API unreachable"
# Fallback to local package or error
exit 1
fi
```
---
## Debugging
### Check Package Availability
```bash
# List all available packages
apk search --all
# Find package by keyword
apk search curl
# Get specific package info
apk info postgresql-client
```
### Verify Installation
```bash
# Check if tool installed
apk info | grep myapp
# Verify PATH
which curl
echo $PATH
```
### Network Testing
```bash
# Test DNS
nslookup api.github.com
# Test connectivity
ping -c1 1.1.1.1
# Test download
curl -I https://api.github.com
```
---
## Contributing
### Adding New Helper Functions
When adding Alpine-specific helpers:
1. Use POSIX shell (ash-compatible)
2. Avoid bash-isms
3. Include error handling
4. Document with examples
5. Test on actual Alpine container
### Improving Package Installation
New patterns could support:
- Automatic Alpine version detection
- Package version pinning
- Dependency resolution
- Conflict detection
---
## Notes
- Alpine uses **ash shell** (POSIX-compatible, not bash)
- Alpine **apk is fast** and has minimal overhead
- Alpine containers **~5MB base image** (vs 100MB+ for Debian)
- **No getent available** by default (use nslookup fallback)
- GitHub releases can be **pre-compiled for Alpine musl**