-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
[core]: alpine‐tools.func
Alpine Linux-specific tool setup and package management module providing helper functions optimized for Alpine's apk package manager and minimal container environment.
- Overview
- Helper Functions
- GitHub Release Functions
- Tool Installation Patterns
- Package Management
- Best Practices
- Debugging
- Contributing
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)
| 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 |
#!/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"Purpose: Converts string to lowercase (portable ash function).
Signature:
lower()Parameters:
-
$1- String to convert
Returns: Lowercase string on stdout
Behavior:
# Alpine's tr works with character classes
printf '%s' "$1" | tr '[:upper:]' '[:lower:]'Usage Examples:
# 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_dirPurpose: Checks if command is available in PATH.
Signature:
has()Parameters:
-
$1- Command name
Returns: 0 if available, 1 if not
Implementation:
has() {
command -v "$1" >/dev/null 2>&1
}Usage Examples:
# 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"Purpose: Ensures specified tools are installed, installs missing ones via apk.
Signature:
need_tool()Parameters:
-
$@- Tool names (space-separated)
Returns: 0 on success, 1 if installation failed
Behavior:
# Checks each tool
# If any missing: runs apk add for all
# Displays message before and afterError Handling:
- Returns 1 if apk add fails
- Shows which tools failed
- Suggests checking package names
Usage Examples:
# 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
}Purpose: Checks if hostname resolves and responds (Alpine-friendly DNS test).
Signature:
net_resolves()Parameters:
-
$1- Hostname to test
Returns: 0 if resolves and responds, 1 if fails
Behavior:
# 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>&1Usage Examples:
# 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
}Purpose: Ensures /usr/local/bin is in PATH across all shell sessions.
Signature:
ensure_usr_local_bin_persist()Parameters: None
Returns: No explicit return value (modifies system)
Behavior:
# 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 automaticallyImplementation:
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"
fiUsage Examples:
# 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 PATHPurpose: Downloads file with progress bar (if pv available) or simple # progress.
Signature:
download_with_progress()Parameters:
-
$1- URL to download -
$2- Destination file path
Returns: 0 on success, 1 on failure
Behavior:
# Attempts to get content-length header
# If available: pipes through pv for progress bar
# If not: uses curl's built-in # progress
# Shows errors clearlyRequirements:
-
curl- For downloading -
pv- Optional, for progress bar - Destination directory must exist
Usage Examples:
# 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
fiPurpose: Checks GitHub releases for available updates and compares with currently installed version.
Signature:
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:
# 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:
~/.${app_lc} # File contains current version string
# Example: ~/.nodejs contains "20.10.0"Usage Examples:
# 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#!/bin/sh
need_tool curl jq # Ensure tools available
# Continue with script#!/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/#!/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 availableAlpine 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 |
# Search for package
apk search myapp
# Show package info
apk info -d myapp
# List available versions
apk search myapp --all# 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# Good: Saves space in container
apk add --no-cache curl git
# Avoid: Wastes space
apk update && apk add curl git# Good: Graceful error
if ! has jq; then
need_tool jq || exit 1
fi
# Using jq safely
jq . < input.json# 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# For custom tools in /usr/local/bin
ensure_usr_local_bin_persist
# Now available in all future shells
/usr/local/bin/my-custom-tool# 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# List all available packages
apk search --all
# Find package by keyword
apk search curl
# Get specific package info
apk info postgresql-client# Check if tool installed
apk info | grep myapp
# Verify PATH
which curl
echo $PATH# Test DNS
nslookup api.github.com
# Test connectivity
ping -c1 1.1.1.1
# Test download
curl -I https://api.github.comWhen adding Alpine-specific helpers:
- Use POSIX shell (ash-compatible)
- Avoid bash-isms
- Include error handling
- Document with examples
- Test on actual Alpine container
New patterns could support:
- Automatic Alpine version detection
- Package version pinning
- Dependency resolution
- Conflict detection
- 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