589 lines
11 KiB
Markdown
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**
|
|
|