diff --git a/README.md b/README.md index c232631..4ea37db 100644 --- a/README.md +++ b/README.md @@ -18,20 +18,40 @@ A GitHub CLI-inspired command-line interface for ClickUp. ## Installation -### Homebrew (macOS/Linux) +### Quick Install + +#### macOS / Linux ```bash -# Coming soon -brew install clickup-cli +curl -sSL https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.sh | bash +``` + +#### Windows (PowerShell) +```powershell +irm https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.ps1 | iex +``` + +### Package Managers + +#### Homebrew (macOS/Linux) +```bash +brew install timimsms/clickup/clickup-cli ``` -### npm +#### npm ```bash # Coming soon npm install -g @clickup/cli ``` -### Direct Download -Download the latest release from the [releases page](https://github.com/tim/cu/releases). +#### Docker +```bash +docker run -it clickup/cli:latest --help +``` + +### Manual Download +Download the latest binary for your platform from the [releases page](https://github.com/timimsms/cu/releases/latest). + +For more installation options and troubleshooting, visit our [installation guide](https://timimsms.github.io/cu/install/). ## Quick Start diff --git a/docs/install/index.html b/docs/install/index.html new file mode 100644 index 0000000..5db2e20 --- /dev/null +++ b/docs/install/index.html @@ -0,0 +1,332 @@ + + + + + + Install ClickUp CLI + + + +
+

Install ClickUp CLI

+ +

The ClickUp CLI (cu) is a command-line tool for managing ClickUp tasks and projects.

+ +
+ + + +
+ +
+
+

Quick Install

+

Run this command in your terminal:

+
+ + curl -sSL https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.sh | bash +
+ +

Using wget

+

If you prefer wget:

+
+ + wget -qO- https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.sh | bash +
+ +
+

Installation Options

+
    +
  • Install specific version: curl -sSL [...] | bash -s -- --version v1.0.0
  • +
  • Install to custom directory: curl -sSL [...] | bash -s -- --dir /usr/local/bin
  • +
  • Show help: curl -sSL [...] | bash -s -- --help
  • +
+
+ +
+ Note: The script will install to ~/.local/bin by default and add it to your PATH if needed. +
+
+
+ +
+
+

Quick Install (PowerShell)

+

Run this command in PowerShell:

+
+ + irm https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.ps1 | iex +
+ +

Alternative Method

+

Or download and run the script:

+
+ + Invoke-WebRequest -Uri https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.ps1 -OutFile install.ps1
./install.ps1
+
+ +
+

Installation Options

+
    +
  • Install specific version: $env:CU_VERSION="v1.0.0"; irm [...] | iex
  • +
  • Install to custom directory: $env:CU_INSTALL_DIR="C:\Program Files\cu"; irm [...] | iex
  • +
+
+ +
+ Security Note: You may need to allow script execution: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +
+
+
+ +
+
+

Homebrew (macOS/Linux)

+
+ + brew install timimsms/clickup/clickup-cli +
+ +

Docker

+
+ + docker run -it clickup/cli:latest --help +
+ +

Manual Download

+

Download the latest binary for your platform from the releases page.

+ +

Build from Source

+
+ + git clone https://github.com/timimsms/cu.git
cd cu
go build -o cu ./cmd/cu
+
+
+
+ +

Getting Started

+

After installation, authenticate with your ClickUp account:

+
+ + cu auth login +
+ +

Then you can start using the CLI:

+
+ + cu task list
cu task create
cu --help
+
+ + +
+ + + + \ No newline at end of file diff --git a/scripts/install.ps1 b/scripts/install.ps1 new file mode 100644 index 0000000..c83f597 --- /dev/null +++ b/scripts/install.ps1 @@ -0,0 +1,313 @@ +# ClickUp CLI Installation Script for Windows +# +# This script installs the ClickUp CLI (cu) on Windows systems +# Usage: +# irm https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.ps1 | iex +# +# You can also specify a version: +# $env:CU_VERSION="v1.0.0"; irm https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.ps1 | iex +# +# Or install to a custom directory: +# $env:CU_INSTALL_DIR="C:\Program Files\cu"; irm https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.ps1 | iex + +param( + [string]$Version = $env:CU_VERSION, + [string]$InstallDir = $env:CU_INSTALL_DIR +) + +# Configuration +$RepoOwner = "timimsms" +$RepoName = "cu" +$BinaryName = "cu" + +# Set defaults +if (-not $Version) { + $Version = "latest" +} + +if (-not $InstallDir) { + $InstallDir = "$env:LOCALAPPDATA\Programs\cu" +} + +# Error handling +$ErrorActionPreference = "Stop" + +# Helper functions +function Write-Info { + param([string]$Message) + Write-Host $Message -ForegroundColor Blue +} + +function Write-Success { + param([string]$Message) + Write-Host $Message -ForegroundColor Green +} + +function Write-Error { + param([string]$Message) + Write-Host "Error: $Message" -ForegroundColor Red +} + +function Write-Warning { + param([string]$Message) + Write-Host $Message -ForegroundColor Yellow +} + +# Detect architecture +function Get-Architecture { + $arch = $env:PROCESSOR_ARCHITECTURE + switch ($arch) { + "AMD64" { return "x86_64" } + "x86" { return "i386" } + "ARM64" { return "arm64" } + default { + Write-Error "Unsupported architecture: $arch" + exit 1 + } + } +} + +# Get the latest version from GitHub +function Get-LatestVersion { + try { + $latestUrl = "https://api.github.com/repos/$RepoOwner/$RepoName/releases/latest" + $response = Invoke-RestMethod -Uri $latestUrl -UseBasicParsing + return $response.tag_name + } + catch { + Write-Error "Failed to get latest version: $_" + exit 1 + } +} + +# Download file with progress +function Download-File { + param( + [string]$Url, + [string]$Output + ) + + try { + Write-Info "Downloading from $Url..." + + # Use Invoke-WebRequest with progress + $ProgressPreference = 'Continue' + Invoke-WebRequest -Uri $Url -OutFile $Output -UseBasicParsing + + if (-not (Test-Path $Output)) { + throw "Download failed - file not found" + } + } + catch { + Write-Error "Failed to download file: $_" + exit 1 + } +} + +# Calculate SHA256 hash +function Get-FileHash256 { + param([string]$FilePath) + + $hash = Get-FileHash -Path $FilePath -Algorithm SHA256 + return $hash.Hash.ToLower() +} + +# Verify checksum +function Verify-Checksum { + param( + [string]$FilePath, + [string]$ChecksumsUrl + ) + + Write-Info "Verifying checksum..." + + # Download checksums file + $checksumsFile = Join-Path $env:TEMP "checksums.txt" + Download-File -Url $ChecksumsUrl -Output $checksumsFile + + # Read checksums + $checksums = Get-Content $checksumsFile + + # Find expected checksum + $fileName = Split-Path $FilePath -Leaf + $expectedLine = $checksums | Where-Object { $_ -match [regex]::Escape($fileName) } + + if (-not $expectedLine) { + Write-Warning "Could not find checksum for $fileName, skipping verification" + Remove-Item $checksumsFile -Force + return + } + + $expectedChecksum = ($expectedLine -split '\s+')[0] + + # Calculate actual checksum + $actualChecksum = Get-FileHash256 -FilePath $FilePath + + # Compare + if ($expectedChecksum -ne $actualChecksum) { + Write-Error "Checksum verification failed!" + Write-Error "Expected: $expectedChecksum" + Write-Error "Actual: $actualChecksum" + Remove-Item $checksumsFile -Force + exit 1 + } + + Write-Success "Checksum verified ✓" + Remove-Item $checksumsFile -Force +} + +# Add to PATH +function Add-ToPath { + param([string]$Directory) + + $userPath = [Environment]::GetEnvironmentVariable("Path", "User") + + if ($userPath -notlike "*$Directory*") { + Write-Info "Adding $Directory to PATH..." + + $newPath = $userPath + if ($newPath -and $newPath[-1] -ne ';') { + $newPath += ';' + } + $newPath += $Directory + + [Environment]::SetEnvironmentVariable("Path", $newPath, "User") + + # Update current session + $env:Path = [Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + $newPath + + Write-Success "Added to PATH ✓" + Write-Warning "Note: You may need to restart your terminal for PATH changes to take effect" + } + else { + Write-Info "$Directory is already in PATH" + } +} + +# Main installation function +function Install-ClickUpCLI { + Write-Host "" + Write-Info "ClickUp CLI Installer for Windows" + Write-Info "=================================" + Write-Host "" + + # Detect architecture + $arch = Get-Architecture + Write-Info "Detected architecture: $arch" + + # Get version to install + if ($Version -eq "latest") { + Write-Info "Fetching latest version..." + $Version = Get-LatestVersion + } + Write-Info "Installing version: $Version" + + # Construct download URL + $platform = "windows_$arch" + $archiveName = "${BinaryName}_${platform}.zip" + $downloadUrl = "https://github.com/$RepoOwner/$RepoName/releases/download/$Version/$archiveName" + $checksumsUrl = "https://github.com/$RepoOwner/$RepoName/releases/download/$Version/checksums.txt" + + # Create temp directory + $tempDir = Join-Path $env:TEMP "cu-install-$(Get-Random)" + New-Item -ItemType Directory -Path $tempDir -Force | Out-Null + + try { + Push-Location $tempDir + + # Download archive + $archivePath = Join-Path $tempDir $archiveName + Download-File -Url $downloadUrl -Output $archivePath + + # Verify checksum + Verify-Checksum -FilePath $archivePath -ChecksumsUrl $checksumsUrl + + # Extract archive + Write-Info "Extracting archive..." + Expand-Archive -Path $archivePath -DestinationPath $tempDir -Force + + # Find the binary + $binaryPath = Join-Path $tempDir "$BinaryName.exe" + if (-not (Test-Path $binaryPath)) { + # Try without .exe extension + $binaryPath = Join-Path $tempDir $BinaryName + if (-not (Test-Path $binaryPath)) { + Write-Error "Binary $BinaryName not found in archive" + exit 1 + } + } + + # Create install directory + if (-not (Test-Path $InstallDir)) { + Write-Info "Creating directory: $InstallDir" + New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null + } + + # Install binary + $targetPath = Join-Path $InstallDir "$BinaryName.exe" + Write-Info "Installing $BinaryName to $InstallDir..." + + # Stop if binary is running + $process = Get-Process -Name $BinaryName -ErrorAction SilentlyContinue + if ($process) { + Write-Warning "Stopping running $BinaryName process..." + Stop-Process -Name $BinaryName -Force + Start-Sleep -Seconds 1 + } + + # Copy binary + Copy-Item -Path $binaryPath -Destination $targetPath -Force + + # Verify installation + if (Test-Path $targetPath) { + Write-Success "Installation successful! ✓" + Write-Host "" + + # Add to PATH + Add-ToPath -Directory $InstallDir + + # Show version + Write-Info "Installed version:" + & $targetPath --version + + Write-Host "" + Write-Info "Get started with:" + Write-Host " $BinaryName --help" + Write-Host " $BinaryName auth login" + + # Install shell completions (optional) + Write-Host "" + Write-Info "To enable PowerShell completions, run:" + Write-Host " $BinaryName completion powershell | Out-String | Invoke-Expression" + Write-Host "" + Write-Host "To make completions persistent, add the above line to your PowerShell profile:" + Write-Host " notepad `$PROFILE" + } + else { + Write-Error "Installation failed" + exit 1 + } + } + finally { + Pop-Location + Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue + } +} + +# Check if running as administrator (not required, but show warning if needed) +$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") + +if (-not $isAdmin -and $InstallDir -like "$env:ProgramFiles*") { + Write-Warning "Installing to $InstallDir requires administrator privileges" + Write-Warning "Run this script as administrator or choose a different install directory" + exit 1 +} + +# Run installation +try { + Install-ClickUpCLI +} +catch { + Write-Error "Installation failed: $_" + exit 1 +} \ No newline at end of file diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..df870a5 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,313 @@ +#!/usr/bin/env bash +# ClickUp CLI Installation Script +# +# This script installs the ClickUp CLI (cu) on Unix-like systems (macOS, Linux) +# Usage: curl -sSL https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.sh | bash +# +# You can also specify a version: +# curl -sSL https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.sh | bash -s -- --version v1.0.0 +# +# Or install to a custom directory: +# curl -sSL https://raw.githubusercontent.com/timimsms/cu/main/scripts/install.sh | bash -s -- --dir /usr/local/bin + +set -euo pipefail + +# Configuration +REPO_OWNER="timimsms" +REPO_NAME="cu" +BINARY_NAME="cu" +INSTALL_DIR="${HOME}/.local/bin" +VERSION="latest" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Helper functions +error() { + echo -e "${RED}Error: $1${NC}" >&2 +} + +success() { + echo -e "${GREEN}$1${NC}" +} + +info() { + echo -e "${BLUE}$1${NC}" +} + +warning() { + echo -e "${YELLOW}$1${NC}" +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --version) + VERSION="$2" + shift 2 + ;; + --dir) + INSTALL_DIR="$2" + shift 2 + ;; + --help) + echo "ClickUp CLI Installation Script" + echo "" + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --version VERSION Install specific version (default: latest)" + echo " --dir DIRECTORY Install to specific directory (default: ~/.local/bin)" + echo " --help Show this help message" + exit 0 + ;; + *) + error "Unknown option: $1" + exit 1 + ;; + esac +done + +# Detect OS and architecture +detect_platform() { + local os arch + + # Detect OS + case "$(uname -s)" in + Darwin) + os="darwin" + ;; + Linux) + os="linux" + ;; + MINGW*|MSYS*|CYGWIN*) + error "Windows detected. Please use install.ps1 instead." + exit 1 + ;; + *) + error "Unsupported operating system: $(uname -s)" + exit 1 + ;; + esac + + # Detect architecture + case "$(uname -m)" in + x86_64|amd64) + arch="x86_64" + ;; + aarch64|arm64) + arch="arm64" + ;; + i386|i686) + arch="i386" + ;; + armv7l|armv7) + arch="armv7" + ;; + *) + error "Unsupported architecture: $(uname -m)" + exit 1 + ;; + esac + + echo "${os}_${arch}" +} + +# Get the latest version from GitHub +get_latest_version() { + local latest_url="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" + + if command -v curl >/dev/null 2>&1; then + curl -sSL "$latest_url" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' + elif command -v wget >/dev/null 2>&1; then + wget -qO- "$latest_url" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' + else + error "Neither curl nor wget found. Please install one of them." + exit 1 + fi +} + +# Download file +download_file() { + local url="$1" + local output="$2" + + if command -v curl >/dev/null 2>&1; then + curl -sSL "$url" -o "$output" + elif command -v wget >/dev/null 2>&1; then + wget -q "$url" -O "$output" + else + error "Neither curl nor wget found. Please install one of them." + exit 1 + fi +} + +# Verify checksum +verify_checksum() { + local file="$1" + local checksums_url="$2" + local expected_checksum + + info "Verifying checksum..." + + # Download checksums file + download_file "$checksums_url" "checksums.txt" + + # Extract expected checksum for our file + expected_checksum=$(grep "$(basename "$file")" checksums.txt | cut -d' ' -f1) + + if [ -z "$expected_checksum" ]; then + warning "Could not find checksum for $(basename "$file"), skipping verification" + rm -f checksums.txt + return 0 + fi + + # Calculate actual checksum + local actual_checksum + if command -v sha256sum >/dev/null 2>&1; then + actual_checksum=$(sha256sum "$file" | cut -d' ' -f1) + elif command -v shasum >/dev/null 2>&1; then + actual_checksum=$(shasum -a 256 "$file" | cut -d' ' -f1) + else + warning "No SHA256 tool found, skipping checksum verification" + rm -f checksums.txt + return 0 + fi + + # Compare checksums + if [ "$expected_checksum" != "$actual_checksum" ]; then + error "Checksum verification failed!" + error "Expected: $expected_checksum" + error "Actual: $actual_checksum" + rm -f checksums.txt + exit 1 + fi + + success "Checksum verified ✓" + rm -f checksums.txt +} + +# Main installation function +main() { + info "ClickUp CLI Installer" + info "====================" + echo "" + + # Detect platform + local platform + platform=$(detect_platform) + info "Detected platform: $platform" + + # Get version to install + if [ "$VERSION" == "latest" ]; then + info "Fetching latest version..." + VERSION=$(get_latest_version) + if [ -z "$VERSION" ]; then + error "Failed to get latest version" + exit 1 + fi + fi + info "Installing version: $VERSION" + + # Construct download URL + local archive_name="${BINARY_NAME}_${platform}.tar.gz" + if [[ "$platform" == *"windows"* ]]; then + archive_name="${BINARY_NAME}_${platform}.zip" + fi + + local download_url="https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download/${VERSION}/${archive_name}" + local checksums_url="https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download/${VERSION}/checksums.txt" + + # Create temporary directory + local temp_dir + temp_dir=$(mktemp -d) + trap "rm -rf $temp_dir" EXIT + + cd "$temp_dir" + + # Download archive + info "Downloading $BINARY_NAME..." + download_file "$download_url" "$archive_name" + + # Verify checksum + verify_checksum "$archive_name" "$checksums_url" + + # Extract archive + info "Extracting archive..." + if [[ "$archive_name" == *.tar.gz ]]; then + tar -xzf "$archive_name" + elif [[ "$archive_name" == *.zip ]]; then + unzip -q "$archive_name" + fi + + # Find the binary + if [ ! -f "$BINARY_NAME" ]; then + error "Binary $BINARY_NAME not found in archive" + exit 1 + fi + + # Create install directory if it doesn't exist + if [ ! -d "$INSTALL_DIR" ]; then + info "Creating directory: $INSTALL_DIR" + mkdir -p "$INSTALL_DIR" + fi + + # Install binary + info "Installing $BINARY_NAME to $INSTALL_DIR..." + chmod +x "$BINARY_NAME" + mv "$BINARY_NAME" "$INSTALL_DIR/" + + # Verify installation + if [ -f "$INSTALL_DIR/$BINARY_NAME" ]; then + success "Installation successful! ✓" + echo "" + + # Check if install directory is in PATH + if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then + warning "Note: $INSTALL_DIR is not in your PATH" + echo "" + echo "Add the following to your shell configuration file:" + echo "" + case "$SHELL" in + */bash) + echo " echo 'export PATH=\"\$PATH:$INSTALL_DIR\"' >> ~/.bashrc" + echo " source ~/.bashrc" + ;; + */zsh) + echo " echo 'export PATH=\"\$PATH:$INSTALL_DIR\"' >> ~/.zshrc" + echo " source ~/.zshrc" + ;; + */fish) + echo " echo 'set -gx PATH \$PATH $INSTALL_DIR' >> ~/.config/fish/config.fish" + echo " source ~/.config/fish/config.fish" + ;; + *) + echo " export PATH=\"\$PATH:$INSTALL_DIR\"" + ;; + esac + echo "" + fi + + # Show version + if [[ ":$PATH:" == *":$INSTALL_DIR:"* ]] || [ -x "$INSTALL_DIR/$BINARY_NAME" ]; then + info "Installed version:" + "$INSTALL_DIR/$BINARY_NAME" --version || true + fi + + echo "" + info "Get started with:" + echo " $BINARY_NAME --help" + echo " $BINARY_NAME auth login" + + else + error "Installation failed" + exit 1 + fi +} + +# Run main function +main \ No newline at end of file diff --git a/scripts/test-install.sh b/scripts/test-install.sh new file mode 100755 index 0000000..38b8b05 --- /dev/null +++ b/scripts/test-install.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# Test script for installation scripts +# This simulates installation without actually downloading from GitHub + +set -euo pipefail + +echo "Testing ClickUp CLI installation script..." +echo "" + +# Test Unix install script +echo "=== Testing Unix install script ===" +echo "" + +# Check script syntax +if bash -n scripts/install.sh; then + echo "✓ Unix install script syntax is valid" +else + echo "✗ Unix install script has syntax errors" + exit 1 +fi + +# Test with dry-run modifications +# Create a modified version that doesn't actually download +cp scripts/install.sh /tmp/test-install.sh + +# Test help output directly +echo "Testing help output..." + +# Test various scenarios +echo "" +echo "Testing --help flag:" +bash /tmp/test-install.sh --help || true + +echo "" +echo "=== Testing Windows install script ===" +echo "" + +# Check PowerShell script syntax (if pwsh is available) +if command -v pwsh >/dev/null 2>&1; then + if pwsh -NoProfile -NonInteractive -Command "& { \$ErrorActionPreference='Stop'; . ./scripts/install.ps1 -WhatIf }" 2>/dev/null; then + echo "✓ Windows install script syntax is valid" + else + echo "⚠ Windows install script syntax check failed (this might be due to platform differences)" + fi +else + echo "⚠ PowerShell not available, skipping Windows script test" +fi + +echo "" +echo "=== Testing installation webpage ===" +echo "" + +# Validate HTML +if command -v tidy >/dev/null 2>&1; then + if tidy -q -e docs/install/index.html 2>/dev/null; then + echo "✓ Installation webpage HTML is valid" + else + echo "⚠ Installation webpage has HTML warnings (non-critical)" + fi +else + echo "⚠ HTML tidy not available, skipping HTML validation" +fi + +# Check if files exist and are non-empty +for file in scripts/install.sh scripts/install.ps1 docs/install/index.html; do + if [ -f "$file" ] && [ -s "$file" ]; then + echo "✓ $file exists and is non-empty" + else + echo "✗ $file is missing or empty" + exit 1 + fi +done + +echo "" +echo "=== Summary ===" +echo "All installation scripts passed basic validation!" +echo "" +echo "To test actual installation:" +echo "1. Push these changes to a branch" +echo "2. Run: curl -sSL https://raw.githubusercontent.com/timimsms/cu/[branch]/scripts/install.sh | bash -s -- --help" +echo "3. Or test locally with: bash scripts/install.sh --dir /tmp/cu-test --version v0.1.0" + +# Cleanup +rm -f /tmp/test-install.sh /tmp/test-install.sh.bak \ No newline at end of file