commit 8e50eef94579d32823df2e25d1cdbdd1820aea8e Author: whuang Date: Fri Apr 3 01:04:30 2026 +0000 Initial commit: Add install scripts and README diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2e26dbf --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Exclude all files from auto-generated source archives (zip/tar.gz) +# This ensures releases only contain the pre-built binaries, not source code +* export-ignore +.gitattributes export-ignore \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b488e9b --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# Aideris CLI Distribution + +Official distribution repository for Aideris CLI binaries and installation scripts. + +## Quick Install + +```bash +curl -fsSL https://gitea.clickthings.net/whuang/aideris-cli-dist/raw/branch/main/install.sh | sh +``` + +## Install Specific Version + +```bash +curl -fsSL https://gitea.clickthings.net/whuang/aideris-cli-dist/raw/branch/main/install.sh | sh -s -- --version v1.0.0 +``` + +## Manual Installation + +1. Download the binary for your platform from [Releases](https://gitea.clickthings.net/whuang/aideris-cli-dist/releases) +2. Make it executable: `chmod +x aideris-cli-*` +3. Move to PATH: `sudo mv aideris-cli-* /usr/local/bin/aideris-cli` + +## Supported Platforms + +- Linux (amd64, arm64, armv7) +- macOS (amd64, arm64) + +## Source Code + +Source code is available at: [whuang/aideris-cli-go](https://gitea.clickthings.net/whuang/aideris-cli-go) + +## Documentation + +For full documentation, visit the [main repository](https://gitea.clickthings.net/whuang/aideris-cli-go). \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..a5175ce --- /dev/null +++ b/install.sh @@ -0,0 +1,359 @@ +#!/bin/sh +# install.sh - Install Aideris CLI from Gitea releases +# Usage: curl -fsSL https://gitea.clickthings.net/whuang/aideris-cli-dist/raw/branch/main/install.sh | sh +# Or: ./install.sh [--version VERSION] [--bin-dir DIR] [--dry-run] [--verbose] + +set -e + +# Configuration +REPO_OWNER="whuang" +REPO_NAME="aideris-cli-dist" +GITEA_URL="https://gitea.clickthings.net" +BINARY_NAME="aideris-cli" +DEFAULT_BIN_DIR="/usr/local/bin" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Parse command line arguments +VERSION="" +BIN_DIR="" +DRY_RUN=false +VERBOSE=false +FORCE=false + +usage() { + cat << 'EOF' +Usage: install.sh [OPTIONS] + +Install Aideris CLI from Gitea releases. + +Options: + --version VERSION Install specific version (default: latest) + --bin-dir DIR Installation directory (default: /usr/local/bin) + --dry-run Show what would be done without executing + --verbose Enable verbose output + --force Force reinstall even if already installed + -h, --help Show this help message + +Examples: + # Install latest version + curl -fsSL https://gitea.clickthings.net/whuang/aideris-cli-dist/raw/branch/main/install.sh | sh + + # Install specific version + ./install.sh --version v1.0.0 + + # Install to custom directory + ./install.sh --bin-dir ~/.local/bin + + # Dry run to preview changes + ./install.sh --dry-run +EOF + exit 0 +} + +while [ $# -gt 0 ]; do + case "$1" in + --version) + VERSION="$2" + shift 2 + ;; + --bin-dir) + BIN_DIR="$2" + shift 2 + ;; + --dry-run) + DRY_RUN=true + shift + ;; + --verbose) + VERBOSE=true + shift + ;; + --force) + FORCE=true + shift + ;; + -h|--help) + usage + ;; + *) + echo "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + ;; + esac +done + +# Set defaults +BIN_DIR="${BIN_DIR:-$DEFAULT_BIN_DIR}" + +# Logging functions +log() { + printf "${BLUE}[INFO]${NC} %s\n" "$1" +} + +log_success() { + printf "${GREEN}[SUCCESS]${NC} %s\n" "$1" +} + +log_warn() { + printf "${YELLOW}[WARN]${NC} %s\n" "$1" +} + +log_error() { + printf "${RED}[ERROR]${NC} %s\n" "$1" +} + +debug() { + if [ "$VERBOSE" = true ]; then + printf "${BLUE}[DEBUG]${NC} %s\n" "$1" + fi +} + +# Detect OS and Architecture +detect_platform() { + OS=$(uname -s | tr '[:upper:]' '[:lower:]') + ARCH=$(uname -m) + + case "$ARCH" in + x86_64|amd64) + ARCH="amd64" + ;; + aarch64|arm64) + ARCH="arm64" + ;; + armv7l|armv7) + ARCH="armv7" + ;; + i386|i686) + ARCH="386" + ;; + *) + log_error "Unsupported architecture: $ARCH" + exit 1 + ;; + esac + + case "$OS" in + linux) + OS="linux" + ;; + darwin) + OS="darwin" + ;; + *) + log_error "Unsupported operating system: $OS" + exit 1 + ;; + esac + + debug "Detected platform: $OS/$ARCH" +} + +# Check if binary is already installed +check_existing_installation() { + if [ -f "${BIN_DIR}/${BINARY_NAME}" ] && [ "$FORCE" = false ]; then + log_warn "${BINARY_NAME} is already installed at ${BIN_DIR}/${BINARY_NAME}" + log "Use --force to reinstall or --version to install a specific version" + exit 0 + fi +} + +# Get the latest version from Gitea +get_latest_version() { + if [ -n "$VERSION" ]; then + return 0 + fi + + log "Fetching latest version from Gitea..." + + # Try to get latest release from Gitea API + API_URL="${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" + debug "API URL: $API_URL" + + LATEST_RELEASE=$(curl -fsSL "$API_URL" 2>/dev/null || echo "") + + if [ -n "$LATEST_RELEASE" ]; then + VERSION=$(echo "$LATEST_RELEASE" | grep -o '"tag_name":"[^"]*"' | head -1 | cut -d'"' -f4) + if [ -n "$VERSION" ]; then + debug "Latest version: $VERSION" + return 0 + fi + fi + + # Fallback: try to get from tags + TAGS_URL="${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/tags" + debug "Tags URL: $TAGS_URL" + + TAGS=$(curl -fsSL "$TAGS_URL" 2>/dev/null || echo "") + if [ -n "$TAGS" ]; then + VERSION=$(echo "$TAGS" | grep -o '"name":"[^"]*"' | head -1 | cut -d'"' -f4) + if [ -n "$VERSION" ]; then + debug "Latest tag: $VERSION" + return 0 + fi + fi + + log_error "Could not determine latest version. Please specify --version." + exit 1 +} + +# Build download URL +build_download_url() { + # Remove 'v' prefix if present for filename + VERSION_NUM="${VERSION#v}" + + # Construct the download URL + # Format: https://gitea.example.com/owner/repo/releases/download/v1.0.0/aideris-cli-linux-amd64 + DOWNLOAD_URL="${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}/releases/download/${VERSION}/${BINARY_NAME}-${OS}-${ARCH}" + + debug "Download URL: $DOWNLOAD_URL" +} + +# Download and install +download_and_install() { + if [ "$DRY_RUN" = true ]; then + log "[DRY RUN] Would download from: $DOWNLOAD_URL" + log "[DRY RUN] Would install to: ${BIN_DIR}/${BINARY_NAME}" + return 0 + fi + + # Create temp directory for download + TEMP_DIR=$(mktemp -d) + DOWNLOAD_PATH="${TEMP_DIR}/${BINARY_NAME}" + + log "Downloading ${BINARY_NAME} ${VERSION} for ${OS}/${ARCH}..." + debug "Downloading to: $DOWNLOAD_PATH" + + # Download the binary + HTTP_CODE=$(curl -fsSL -w "%{http_code}" -o "$DOWNLOAD_PATH" "$DOWNLOAD_URL" 2>/dev/null || echo "000") + + if [ "$HTTP_CODE" != "200" ]; then + rm -f "$DOWNLOAD_PATH" + log_error "Failed to download binary (HTTP $HTTP_CODE)" + log_error "URL: $DOWNLOAD_URL" + log "Available platforms are typically:" + log " - aideris-cli-linux-amd64" + log " - aideris-cli-linux-arm64" + log " - aideris-cli-darwin-amd64" + log " - aideris-cli-darwin-arm64" + rm -rf "$TEMP_DIR" + exit 1 + fi + + # Make executable + chmod +x "$DOWNLOAD_PATH" + + # Verify binary is valid ELF/Mach-O + if ! file "$DOWNLOAD_PATH" | grep -qE "(ELF|Mach-O)"; then + log_warn "Binary file type check failed (may still be valid)" + debug "File type: $(file "$DOWNLOAD_PATH")" + fi + + # Create bin directory if it doesn't exist + if [ ! -d "$BIN_DIR" ]; then + log "Creating directory: $BIN_DIR" + if [ "$BIN_DIR" = "$DEFAULT_BIN_DIR" ]; then + # For system directories, need sudo + log "This requires root privileges." + echo -n "Use sudo? [y/N]: " + read -r answer + case "$answer" in + [yY]|[yY][eE][sS]) + sudo mkdir -p "$BIN_DIR" + ;; + *) + rm -rf "$TEMP_DIR" + log_error "Installation cancelled" + exit 1 + ;; + esac + else + mkdir -p "$BIN_DIR" + fi + fi + + # Install the binary + log "Installing ${BINARY_NAME} to ${BIN_DIR}..." + if [ "$BIN_DIR" = "$DEFAULT_BIN_DIR" ] && [ ! -w "$BIN_DIR" ]; then + sudo cp "$DOWNLOAD_PATH" "${BIN_DIR}/${BINARY_NAME}" + sudo chmod 755 "${BIN_DIR}/${BINARY_NAME}" + else + cp "$DOWNLOAD_PATH" "${BIN_DIR}/${BINARY_NAME}" + chmod 755 "${BIN_DIR}/${BINARY_NAME}" + fi + + # Cleanup + rm -rf "$TEMP_DIR" +} + +# Verify installation +verify_installation() { + if [ "$DRY_RUN" = true ]; then + log "[DRY RUN] Would verify installation" + return 0 + fi + + if [ -f "${BIN_DIR}/${BINARY_NAME}" ]; then + log_success "Installation complete!" + + # Check if bin dir is in PATH + case ":$PATH:" in + *":${BIN_DIR}:"*) + ;; + *) + log_warn "${BIN_DIR} is not in your PATH" + log "Add it with: export PATH=\"${BIN_DIR}:\$PATH\"" + ;; + esac + + # Show version + log "Installing ${BINARY_NAME} version ${VERSION} to ${BIN_DIR}" + echo "" + echo "Verify installation with:" + echo " ${BIN_DIR}/${BINARY_NAME} --help" + echo "" + echo "Quick start:" + echo " ${BINARY_NAME} onboard --email \"your@email.com\"" + else + log_error "Installation failed - binary not found at ${BIN_DIR}/${BINARY_NAME}" + exit 1 + fi +} + +# Main execution +main() { + echo "" + echo "========================================" + echo " Aideris CLI Installer" + echo "========================================" + echo "" + + # Detect platform + detect_platform + log "Platform detected: ${OS}/${ARCH}" + + # Check existing installation + check_existing_installation + + # Get version + get_latest_version + log "Target version: ${VERSION}" + + # Build download URL + build_download_url + + # Download and install + download_and_install + + # Verify installation + verify_installation +} + +# Run main +main \ No newline at end of file diff --git a/push-release.sh b/push-release.sh new file mode 100755 index 0000000..b20ca3d --- /dev/null +++ b/push-release.sh @@ -0,0 +1,201 @@ +#!/bin/bash +# push-release.sh - Push an existing release to Gitea +# Usage: ./push-release.sh --version v1.0.0 --gitea-token TOKEN + +set -e + +# Configuration +BINARY_NAME="aideris-cli" +GITEA_URL="https://gitea.clickthings.net" +REPO_OWNER="whuang" +REPO_NAME="aideris-cli-dist" + +# Parse arguments +VERSION="" +GITEA_TOKEN="" + +while [ $# -gt 0 ]; do + case "$1" in + --version) + VERSION="$2" + shift 2 + ;; + --gitea-token) + GITEA_TOKEN="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +if [ -z "$VERSION" ]; then + echo "Error: --version is required" + echo "Usage: ./push-release.sh --version v1.0.0 --gitea-token TOKEN" + exit 1 +fi + +if [ -z "$GITEA_TOKEN" ]; then + echo "Error: --gitea-token is required" + echo "Usage: ./push-release.sh --version v1.0.0 --gitea-token TOKEN" + exit 1 +fi + +BUILD_DIR="dist/${VERSION}" + +if [ ! -d "$BUILD_DIR" ]; then + echo "Error: Build directory not found: $BUILD_DIR" + echo "Run the build first to create binaries" + exit 1 +fi + +echo "========================================" +echo " Aideris CLI Release Push" +echo "========================================" +echo "" +echo "Version: $VERSION" +echo "Build dir: $BUILD_DIR" +echo "" + +# Check if release notes exist +RELEASE_NOTES="${BUILD_DIR}/RELEASE_NOTES.md" +if [ ! -f "$RELEASE_NOTES" ]; then + echo "Warning: Release notes not found at $RELEASE_NOTES" + echo "Creating default release notes..." + cat > "$RELEASE_NOTES" << EOF +# Aideris CLI ${VERSION} + +## Installation + +### Quick Install +\`\`\`bash +curl -fsSL https://gitea.clickthings.net/${REPO_OWNER}/${REPO_NAME}/raw/branch/main/install.sh | sh +\`\`\` + +### Manual Download +Download the binary for your platform from the Assets below. +EOF +fi + +# Check if checksums exist +CHECKSUMS_FILE="${BUILD_DIR}/checksums.txt" +if [ ! -f "$CHECKSUMS_FILE" ]; then + echo "Generating checksums..." + cd "$BUILD_DIR" + sha256sum ${BINARY_NAME}-* > checksums.txt 2>/dev/null || true + cd - > /dev/null +fi + +echo "Pushing to Gitea..." + +# Function to make API calls with error handling +gitea_api() { + local method=$1 + local endpoint=$2 + local data=$3 + + if [ -z "$data" ]; then + curl -fsSL -X "$method" \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}${endpoint}" 2>&1 + else + curl -fsSL -X "$method" \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "$data" \ + "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}${endpoint}" 2>&1 + fi +} + +# Check if release already exists +echo "Checking for existing release..." +EXISTING_RELEASE=$(gitea_api "GET" "/releases/tags/${VERSION}" 2>/dev/null || echo "{}") +RELEASE_ID=$(echo "$EXISTING_RELEASE" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2) + +if [ -n "$RELEASE_ID" ]; then + echo " ✓ Found existing release: ID $RELEASE_ID" +else + echo " Creating new release..." + + # Escape release notes for JSON + ESCAPED_BODY=$(cat "$RELEASE_NOTES" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read())[1:-1])' 2>/dev/null || cat "$RELEASE_NOTES" | sed 's/"/\\"/g' | tr '\n' ' ') + + RELEASE_PAYLOAD=$(cat << EOF +{ + "tag_name": "${VERSION}", + "name": "${VERSION}", + "body": "${ESCAPED_BODY}", + "draft": false, + "prerelease": false +} +EOF +) + + CREATE_RESPONSE=$(gitea_api "POST" "/releases" "$RELEASE_PAYLOAD" 2>&1 || true) + RELEASE_ID=$(echo "$CREATE_RESPONSE" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2) + + if [ -n "$RELEASE_ID" ]; then + echo " ✓ Release created: ID $RELEASE_ID" + else + echo " ✗ Failed to create release" + echo "Response: $CREATE_RESPONSE" + exit 1 + fi +fi + +# Get list of existing assets +echo "" +echo "Fetching existing assets..." +EXISTING_ASSETS=$(gitea_api "GET" "/releases/${RELEASE_ID}/assets" 2>/dev/null || echo "[]") + +# Upload assets +echo "" +echo "Uploading assets..." + +upload_count=0 +skip_count=0 + +for asset in ${BUILD_DIR}/${BINARY_NAME}-* ${BUILD_DIR}/checksums.txt; do + if [ -f "$asset" ]; then + FILENAME=$(basename "$asset") + + # Check if asset already exists + ASSET_EXISTS=$(echo "$EXISTING_ASSETS" | grep -o "\"name\":\"${FILENAME}\"" | head -1 || echo "") + + if [ -n "$ASSET_EXISTS" ]; then + echo " ⊘ Skipping (exists): $FILENAME" + skip_count=$((skip_count + 1)) + else + echo " ↑ Uploading: $FILENAME..." + + UPLOAD_RESPONSE=$(curl -fsSL -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/octet-stream" \ + --data-binary @"$asset" \ + "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/${RELEASE_ID}/assets?name=${FILENAME}" 2>&1 || true) + + UPLOADED_ID=$(echo "$UPLOAD_RESPONSE" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2) + + if [ -n "$UPLOADED_ID" ]; then + echo " ✓ Uploaded: $FILENAME" + upload_count=$((upload_count + 1)) + else + echo " ✗ Failed to upload: $FILENAME" + echo " Response: $UPLOAD_RESPONSE" + fi + fi + fi +done + +echo "" +echo "========================================" +echo " Push Complete" +echo "========================================" +echo "" +echo "Uploaded: $upload_count asset(s)" +echo "Skipped: $skip_count existing asset(s)" +echo "" +echo "Release URL: ${GITEA_URL}/${REPO_OWNER}/${REPO_NAME}/releases/tag/${VERSION}" \ No newline at end of file