diff --git a/[core]:-alpine‐tools.func.md b/[core]:-alpine‐tools.func.md new file mode 100644 index 0000000..5271469 --- /dev/null +++ b/[core]:-alpine‐tools.func.md @@ -0,0 +1,588 @@ +# 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** +