install.sh 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. REPO_OWNER="christianlempa"
  4. REPO_NAME="boilerplates"
  5. VERSION="${VERSION:-latest}"
  6. TARGET_DIR="${TARGET_DIR:-$HOME/boilerplates}"
  7. usage() {
  8. cat <<USAGE
  9. Usage: install.sh [OPTIONS]
  10. Install the boilerplates CLI from GitHub releases.
  11. Options:
  12. --path DIR Installation directory (default: "$HOME/boilerplates")
  13. --version VER Version to install (default: "latest")
  14. -h, --help Show this message
  15. Examples:
  16. curl -fsSL https://raw.githubusercontent.com/christianlempa/boilerplates/main/scripts/install.sh | bash
  17. curl -fsSL https://raw.githubusercontent.com/christianlempa/boilerplates/main/scripts/install.sh | bash -s -- --version v1.0.0
  18. USAGE
  19. }
  20. log() { printf '[boilerplates] %s\n' "$*" >&2; }
  21. error() { printf '[boilerplates][error] %s\n' "$*" >&2; exit 1; }
  22. check_dependencies() {
  23. command -v tar >/dev/null 2>&1 || error "tar is required but not found"
  24. command -v python3 >/dev/null 2>&1 || error "Python 3 is required. Install: sudo apt install python3 python3-pip"
  25. python3 -m pip --version >/dev/null 2>&1 || error "pip is required. Install: sudo apt install python3-pip"
  26. if command -v pipx >/dev/null 2>&1; then
  27. PIPX_CMD="pipx"
  28. elif [[ -x "$(python3 -m site --user-base 2>/dev/null)/bin/pipx" ]]; then
  29. PIPX_CMD="$(python3 -m site --user-base)/bin/pipx"
  30. else
  31. error "pipx is required. Install: pip install --user pipx"
  32. fi
  33. log "✓ All dependencies available"
  34. }
  35. parse_args() {
  36. while [[ $# -gt 0 ]]; do
  37. case "$1" in
  38. --path) TARGET_DIR="$2"; shift 2 ;;
  39. --version) VERSION="$2"; shift 2 ;;
  40. -h|--help) usage; exit 0 ;;
  41. *) error "Unknown option: $1" ;;
  42. esac
  43. done
  44. }
  45. get_latest_release() {
  46. local api_url="https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/releases/latest"
  47. if command -v curl >/dev/null 2>&1; then
  48. curl -fsSL "$api_url" | grep '"tag_name":' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/'
  49. elif command -v wget >/dev/null 2>&1; then
  50. wget -qO- "$api_url" | grep '"tag_name":' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/'
  51. else
  52. echo "error" >&2; return 1
  53. fi
  54. }
  55. download_release() {
  56. local version="$1"
  57. # Resolve "latest" to actual version
  58. if [[ "$version" == "latest" ]]; then
  59. log "Fetching latest release..."
  60. version=$(get_latest_release)
  61. [[ "$version" =~ ^error ]] && error "Failed to fetch latest release"
  62. log "Latest version: $version"
  63. fi
  64. # Ensure 'v' prefix
  65. [[ "$version" =~ ^v ]] || version="v$version"
  66. local url="https://github.com/$REPO_OWNER/$REPO_NAME/archive/refs/tags/$version.tar.gz"
  67. local temp_dir=$(mktemp -d)
  68. local archive="$temp_dir/release.tar.gz"
  69. log "Downloading $version..."
  70. if command -v curl >/dev/null 2>&1; then
  71. curl -fsSL -o "$archive" "$url" || { rm -rf "$temp_dir"; error "Download failed"; }
  72. elif command -v wget >/dev/null 2>&1; then
  73. wget -qO "$archive" "$url" || { rm -rf "$temp_dir"; error "Download failed"; }
  74. fi
  75. log "Extracting to $TARGET_DIR..."
  76. [[ -d "$TARGET_DIR" ]] && rm -rf "$TARGET_DIR"
  77. mkdir -p "$(dirname "$TARGET_DIR")"
  78. tar -xzf "$archive" -C "$(dirname "$TARGET_DIR")" || { rm -rf "$temp_dir"; error "Extraction failed"; }
  79. mv "$(dirname "$TARGET_DIR")/$REPO_NAME-${version#v}" "$TARGET_DIR" || { rm -rf "$temp_dir"; error "Installation failed"; }
  80. rm -rf "$temp_dir"
  81. echo "$version" > "$TARGET_DIR/.version"
  82. log "✓ Release extracted"
  83. }
  84. install_cli() {
  85. log "Installing CLI via pipx..."
  86. "$PIPX_CMD" ensurepath 2>&1 | grep -v "^$" || true
  87. "$PIPX_CMD" install --editable --force "$TARGET_DIR"
  88. }
  89. main() {
  90. parse_args "$@"
  91. log "Checking dependencies..."
  92. check_dependencies
  93. TARGET_DIR=$(python3 -c "import os, sys; print(os.path.abspath(os.path.expanduser('$TARGET_DIR')))")
  94. download_release "$VERSION"
  95. install_cli
  96. local version=$(cat "$TARGET_DIR/.version" 2>/dev/null || echo "unknown")
  97. cat <<EOF
  98. ✓ Installation complete!
  99. Version: $version
  100. Location: $TARGET_DIR
  101. Usage:
  102. boilerplates --help
  103. boilerplates compose list
  104. boilerplates compose generate <template>
  105. Update:
  106. curl -fsSL https://raw.githubusercontent.com/$REPO_OWNER/$REPO_NAME/main/scripts/install.sh | bash
  107. EOF
  108. }
  109. main "$@"