|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Installer for Docker CLI plugins (e.g., compose, buildx) |
| 3 | +# Installs plugins to ~/.docker/cli-plugins/ |
| 4 | +set -euo pipefail |
| 5 | + |
| 6 | +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" |
| 7 | +. "$DIR/lib/common.sh" |
| 8 | +. "$DIR/lib/install_strategy.sh" |
| 9 | + |
| 10 | +TOOL="${1:-}" |
| 11 | +if [ -z "$TOOL" ]; then |
| 12 | + echo "Usage: $0 TOOL_NAME" >&2 |
| 13 | + exit 1 |
| 14 | +fi |
| 15 | + |
| 16 | +CATALOG_FILE="$DIR/../catalog/$TOOL.json" |
| 17 | +if [ ! -f "$CATALOG_FILE" ]; then |
| 18 | + echo "Error: Catalog file not found: $CATALOG_FILE" >&2 |
| 19 | + exit 1 |
| 20 | +fi |
| 21 | + |
| 22 | +# Parse catalog |
| 23 | +BINARY_NAME="$(jq -r '.binary_name' "$CATALOG_FILE")" |
| 24 | +GITHUB_REPO="$(jq -r '.github_repo // empty' "$CATALOG_FILE")" |
| 25 | +PLUGIN_NAME="$(jq -r '.plugin_name // .name' "$CATALOG_FILE")" |
| 26 | +VERSION_COMMAND="$(jq -r '.version_command // empty' "$CATALOG_FILE")" |
| 27 | + |
| 28 | +# Docker CLI plugins go to ~/.docker/cli-plugins/ |
| 29 | +PLUGIN_DIR="$HOME/.docker/cli-plugins" |
| 30 | +mkdir -p "$PLUGIN_DIR" |
| 31 | + |
| 32 | +# Get current version |
| 33 | +before="" |
| 34 | +if [ -n "$VERSION_COMMAND" ]; then |
| 35 | + before="$(eval "$VERSION_COMMAND" 2>/dev/null || true)" |
| 36 | +elif [ -f "$PLUGIN_DIR/docker-$PLUGIN_NAME" ]; then |
| 37 | + before="$(docker $PLUGIN_NAME version 2>/dev/null | head -1 || true)" |
| 38 | +fi |
| 39 | + |
| 40 | +# Detect OS and architecture |
| 41 | +OS="$(uname -s | tr '[:upper:]' '[:lower:]')" |
| 42 | +ARCH_RAW="$(uname -m)" |
| 43 | + |
| 44 | +# Map architecture to Docker's naming convention |
| 45 | +case "$ARCH_RAW" in |
| 46 | + x86_64) ARCH="x86_64" ;; |
| 47 | + aarch64) ARCH="aarch64" ;; |
| 48 | + arm64) ARCH="aarch64" ;; |
| 49 | + armv7l) ARCH="armv7" ;; |
| 50 | + armv6l) ARCH="armv6" ;; |
| 51 | + *) ARCH="$ARCH_RAW" ;; |
| 52 | +esac |
| 53 | + |
| 54 | +# Resolve latest version from GitHub |
| 55 | +LATEST="" |
| 56 | +if [ -n "$GITHUB_REPO" ]; then |
| 57 | + LATEST="$(curl -fsSIL -H "User-Agent: cli-audit" -o /dev/null -w '%{url_effective}' \ |
| 58 | + "https://github.com/$GITHUB_REPO/releases/latest" 2>/dev/null | awk -F'/' '{print $NF}')" |
| 59 | +fi |
| 60 | + |
| 61 | +if [ -z "$LATEST" ]; then |
| 62 | + echo "[$TOOL] Error: Unable to resolve latest version" >&2 |
| 63 | + echo "[$TOOL] before: ${before:-<none>}" >&2 |
| 64 | + exit 1 |
| 65 | +fi |
| 66 | + |
| 67 | +# Build download URL |
| 68 | +# Docker Compose uses: docker-compose-{os}-{arch} |
| 69 | +DOWNLOAD_URL="https://github.com/$GITHUB_REPO/releases/download/$LATEST/docker-$PLUGIN_NAME-$OS-$ARCH" |
| 70 | + |
| 71 | +# Download |
| 72 | +tmpfile="/tmp/docker-$PLUGIN_NAME.$$" |
| 73 | +rm -f "$tmpfile" |
| 74 | + |
| 75 | +echo "[$TOOL] Downloading $DOWNLOAD_URL" |
| 76 | +if ! curl -fL --retry 3 --retry-delay 1 --connect-timeout 10 -o "$tmpfile" "$DOWNLOAD_URL" 2>/dev/null; then |
| 77 | + echo "[$TOOL] Error: Download failed from $DOWNLOAD_URL" >&2 |
| 78 | + rm -f "$tmpfile" |
| 79 | + exit 1 |
| 80 | +fi |
| 81 | + |
| 82 | +# Validate download |
| 83 | +if ! [ -s "$tmpfile" ]; then |
| 84 | + echo "[$TOOL] Error: Downloaded file is empty" >&2 |
| 85 | + rm -f "$tmpfile" |
| 86 | + exit 1 |
| 87 | +fi |
| 88 | + |
| 89 | +# Install to Docker CLI plugins directory |
| 90 | +chmod +x "$tmpfile" |
| 91 | +mv "$tmpfile" "$PLUGIN_DIR/docker-$PLUGIN_NAME" |
| 92 | + |
| 93 | +# Report |
| 94 | +after="" |
| 95 | +if [ -n "$VERSION_COMMAND" ]; then |
| 96 | + after="$(eval "$VERSION_COMMAND" 2>/dev/null || true)" |
| 97 | +else |
| 98 | + after="$(docker $PLUGIN_NAME version 2>/dev/null | head -1 || true)" |
| 99 | +fi |
| 100 | + |
| 101 | +printf "[%s] before: %s\n" "$TOOL" "${before:-<none>}" |
| 102 | +printf "[%s] after: %s\n" "$TOOL" "${after:-<none>}" |
| 103 | +printf "[%s] path: %s\n" "$TOOL" "$PLUGIN_DIR/docker-$PLUGIN_NAME" |
| 104 | + |
| 105 | +# Refresh snapshot after successful installation |
| 106 | +refresh_snapshot "$TOOL" |
0 commit comments