Documentation
This commit is contained in:
588
docs/alpine-tools.func.md
Normal file
588
docs/alpine-tools.func.md
Normal file
@@ -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**
|
||||
|
||||
Reference in New Issue
Block a user