From 44cf868b88d5f65896c5cc4cbdf70895e4687acb Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 3 Feb 2026 07:00:52 +0000 Subject: [PATCH] feat(build): add tar.xz support and unified installer scripts - Add tar.xz archive support using Borg's compress package - ArchiveXZ() and ArchiveWithFormat() for configurable compression - Better compression ratio than gzip for release artifacts - Consolidate 12 installer scripts into 2 unified scripts - install.sh and install.bat with BunnyCDN edge variable support - Subdomains: setup.core.help, ci.core.help, dev.core.help, etc. - MODE and VARIANT transformed at edge based on subdomain - Installers prefer tar.xz with automatic fallback to tar.gz - Fixed CodeRabbit issues: HTTP status patterns, tar error handling, verify_install params, VARIANT validation, CI PATH persistence Co-Authored-By: Claude Opus 4.5 --- go.mod | 1 + go.sum | 2 + install.bat | 180 +++++++++++++++++++++++++++++++ install.sh | 222 ++++++++++++++++++++++++++++++++++++++ pkg/build/archive.go | 112 +++++++++++++++++-- pkg/build/archive_test.go | 84 +++++++++++++++ tools/install/agent.bat | 72 ------------- tools/install/agent.sh | 50 --------- tools/install/ci.bat | 57 ---------- tools/install/ci.sh | 50 --------- tools/install/dev.bat | 69 ------------ tools/install/dev.sh | 46 -------- tools/install/go.bat | 72 ------------- tools/install/go.sh | 51 --------- tools/install/php.bat | 72 ------------- tools/install/php.sh | 50 --------- tools/install/setup.bat | 85 --------------- tools/install/setup.sh | 80 -------------- 18 files changed, 595 insertions(+), 760 deletions(-) create mode 100644 install.bat create mode 100644 install.sh delete mode 100644 tools/install/agent.bat delete mode 100644 tools/install/agent.sh delete mode 100644 tools/install/ci.bat delete mode 100644 tools/install/ci.sh delete mode 100644 tools/install/dev.bat delete mode 100644 tools/install/dev.sh delete mode 100644 tools/install/go.bat delete mode 100644 tools/install/go.sh delete mode 100644 tools/install/php.bat delete mode 100644 tools/install/php.sh delete mode 100644 tools/install/setup.bat delete mode 100644 tools/install/setup.sh diff --git a/go.mod b/go.mod index 04725dc6..b8665c65 100644 --- a/go.mod +++ b/go.mod @@ -58,6 +58,7 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/ugorji/go/codec v1.3.0 // indirect + github.com/ulikunitz/xz v0.5.15 // indirect github.com/wI2L/jsondiff v0.7.0 // indirect github.com/woodsbury/decimal128 v1.4.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect diff --git a/go.sum b/go.sum index 1402b11e..3fb17e72 100644 --- a/go.sum +++ b/go.sum @@ -142,6 +142,8 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= +github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/wI2L/jsondiff v0.7.0 h1:1lH1G37GhBPqCfp/lrs91rf/2j3DktX6qYAKZkLuCQQ= github.com/wI2L/jsondiff v0.7.0/go.mod h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM= github.com/woodsbury/decimal128 v1.4.0 h1:xJATj7lLu4f2oObouMt2tgGiElE5gO6mSWUjQsBgUlc= diff --git a/install.bat b/install.bat new file mode 100644 index 00000000..490ba977 --- /dev/null +++ b/install.bat @@ -0,0 +1,180 @@ +@echo off +REM Core CLI unified installer (Windows) +REM Served via *.core.help with BunnyCDN edge transformation +REM +REM Usage: +REM curl -fsSL setup.core.help -o install.bat && install.bat # Interactive (default) +REM curl -fsSL ci.core.help -o install.bat && install.bat # CI/CD +REM curl -fsSL dev.core.help -o install.bat && install.bat # Full development +REM curl -fsSL go.core.help -o install.bat && install.bat # Go variant +REM curl -fsSL php.core.help -o install.bat && install.bat # PHP variant +REM curl -fsSL agent.core.help -o install.bat && install.bat # Agent variant +REM +setlocal enabledelayedexpansion + +REM === BunnyCDN Edge Variables (transformed at edge based on subdomain) === +set "MODE={{CORE_MODE}}" +set "VARIANT={{CORE_VARIANT}}" + +REM === Fallback for local testing === +if "!MODE!"=="{{CORE_MODE}}" ( + if defined CORE_MODE (set "MODE=!CORE_MODE!") else (set "MODE=setup") +) +if "!VARIANT!"=="{{CORE_VARIANT}}" ( + if defined CORE_VARIANT (set "VARIANT=!CORE_VARIANT!") else (set "VARIANT=") +) + +REM === Configuration === +set "VERSION=%~1" +if "%VERSION%"=="" set "VERSION=latest" +set "REPO=host-uk/core" +set "BINARY=core" +set "INSTALL_DIR=%LOCALAPPDATA%\Programs\core" + +REM === Resolve Version === +if "%VERSION%"=="latest" ( + for /f "tokens=2 delims=:" %%a in ('curl -fsSL "https://api.github.com/repos/%REPO%/releases/latest" ^| findstr "tag_name"') do ( + set "VERSION=%%a" + set "VERSION=!VERSION:"=!" + set "VERSION=!VERSION: =!" + set "VERSION=!VERSION:,=!" + ) + if "!VERSION!"=="" ( + echo ERROR: Failed to fetch latest version + exit /b 1 + ) + if "!VERSION!"=="latest" ( + echo ERROR: Failed to resolve version + exit /b 1 + ) +) + +REM === Create install directory === +if not exist "%INSTALL_DIR%" mkdir "%INSTALL_DIR%" + +REM === Mode dispatch === +if "%MODE%"=="ci" goto :install_ci +if "%MODE%"=="dev" goto :install_dev +if "%MODE%"=="variant" goto :install_variant +goto :install_setup + +:install_setup +echo Installing %BINARY% !VERSION! for Windows... +call :find_archive "" ARCHIVE +call :download_and_extract +call :install_binary +call :verify_install +goto :done + +:install_ci +echo Installing %BINARY% !VERSION! (CI)... +call :find_archive "" ARCHIVE + +REM Download +curl -fsSL "https://github.com/%REPO%/releases/download/!VERSION!/!ARCHIVE!" -o "%TEMP%\!ARCHIVE!" +if errorlevel 1 ( + echo ERROR: Failed to download !ARCHIVE! + exit /b 1 +) + +REM Extract - try System32 first (CI often has admin), else local +powershell -Command "try { Expand-Archive -Force '%TEMP%\!ARCHIVE!' '%TEMP%\core-extract' } catch { exit 1 }" +if errorlevel 1 ( + echo ERROR: Failed to extract archive + del "%TEMP%\!ARCHIVE!" 2>nul + exit /b 1 +) + +move /y "%TEMP%\core-extract\%BINARY%.exe" "C:\Windows\System32\%BINARY%.exe" >nul 2>&1 +if errorlevel 1 ( + if not exist "%INSTALL_DIR%" mkdir "%INSTALL_DIR%" + move /y "%TEMP%\core-extract\%BINARY%.exe" "%INSTALL_DIR%\%BINARY%.exe" + echo %PATH% | findstr /i /c:"%INSTALL_DIR%" >nul + if errorlevel 1 ( + powershell -Command "[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path', 'User') + ';%INSTALL_DIR%', 'User')" + set "PATH=%PATH%;%INSTALL_DIR%" + ) +) +rmdir /s /q "%TEMP%\core-extract" 2>nul +del "%TEMP%\!ARCHIVE!" 2>nul + +%BINARY% --version || exit /b 1 +goto :done + +:install_dev +echo Installing %BINARY% !VERSION! (full) for Windows... +call :find_archive "" ARCHIVE +call :download_and_extract +call :install_binary +call :verify_install +echo. +echo Full development variant installed. Available commands: +echo core dev - Multi-repo workflows +echo core build - Cross-platform builds +echo core release - Build and publish releases +goto :done + +:install_variant +echo Installing %BINARY% !VERSION! (%VARIANT% variant) for Windows... +call :find_archive "%VARIANT%" ARCHIVE +call :download_and_extract +call :install_binary +call :verify_install +goto :done + +REM === Helper Functions === + +:find_archive +set "_variant=%~1" +set "_result=%~2" + +REM Try variant-specific first, then full +if not "%_variant%"=="" ( + set "_try=%BINARY%-%_variant%-windows-amd64.zip" + curl -fsSLI "https://github.com/%REPO%/releases/download/!VERSION!/!_try!" 2>nul | findstr /r "HTTP/.* [23]0[02]" >nul + if not errorlevel 1 ( + set "%_result%=!_try!" + exit /b 0 + ) + echo Using full variant ^(%_variant% variant not available^) +) + +set "%_result%=%BINARY%-windows-amd64.zip" +exit /b 0 + +:download_and_extract +curl -fsSL "https://github.com/%REPO%/releases/download/!VERSION!/!ARCHIVE!" -o "%TEMP%\!ARCHIVE!" +if errorlevel 1 ( + echo ERROR: Failed to download !ARCHIVE! + exit /b 1 +) + +powershell -Command "try { Expand-Archive -Force '%TEMP%\!ARCHIVE!' '%INSTALL_DIR%' } catch { exit 1 }" +if errorlevel 1 ( + echo ERROR: Failed to extract archive + del "%TEMP%\!ARCHIVE!" 2>nul + exit /b 1 +) +del "%TEMP%\!ARCHIVE!" 2>nul +exit /b 0 + +:install_binary +REM Add to PATH using PowerShell (avoids setx 1024 char limit) +echo %PATH% | findstr /i /c:"%INSTALL_DIR%" >nul +if errorlevel 1 ( + powershell -Command "[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path', 'User') + ';%INSTALL_DIR%', 'User')" + set "PATH=%PATH%;%INSTALL_DIR%" +) +exit /b 0 + +:verify_install +if not exist "%INSTALL_DIR%\%BINARY%.exe" ( + echo ERROR: Installation failed - binary not found + exit /b 1 +) +"%INSTALL_DIR%\%BINARY%.exe" --version +if errorlevel 1 exit /b 1 +exit /b 0 + +:done +endlocal diff --git a/install.sh b/install.sh new file mode 100644 index 00000000..f7ad41db --- /dev/null +++ b/install.sh @@ -0,0 +1,222 @@ +#!/bin/bash +# Core CLI unified installer +# Served via *.core.help with BunnyCDN edge transformation +# +# Usage: +# curl -fsSL setup.core.help | bash # Interactive setup (default) +# curl -fsSL ci.core.help | bash # CI/CD (minimal, fast) +# curl -fsSL dev.core.help | bash # Full development +# curl -fsSL go.core.help | bash # Go development variant +# curl -fsSL php.core.help | bash # PHP/Laravel variant +# curl -fsSL agent.core.help | bash # AI agent variant +# +# Version override: +# curl -fsSL setup.core.help | bash -s -- v1.0.0 +# +set -eo pipefail + +# === BunnyCDN Edge Variables (transformed at edge based on subdomain) === +MODE="{{CORE_MODE}}" # setup, ci, dev, variant +VARIANT="{{CORE_VARIANT}}" # go, php, agent (when MODE=variant) + +# === User overrides (fallback for local testing) === +[[ "$MODE" == "{{CORE_MODE}}" ]] && MODE="${CORE_MODE:-setup}" +[[ "$VARIANT" == "{{CORE_VARIANT}}" ]] && VARIANT="${CORE_VARIANT:-}" + +# === Configuration === +VERSION="${1:-latest}" +REPO="host-uk/core" +BINARY="core" + +# === Colours === +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +DIM='\033[2m' +BOLD='\033[1m' +NC='\033[0m' + +info() { echo -e "${BLUE}>>>${NC} $1"; } +success() { echo -e "${GREEN}>>>${NC} $1"; } +error() { echo -e "${RED}>>>${NC} $1" >&2; exit 1; } +dim() { echo -e "${DIM}$1${NC}"; } + +# === Platform Detection === +detect_platform() { + OS="$(uname -s | tr '[:upper:]' '[:lower:]')" + ARCH="$(uname -m)" + + case "$ARCH" in + x86_64|amd64) ARCH="amd64" ;; + arm64|aarch64) ARCH="arm64" ;; + *) error "Unsupported architecture: $ARCH" ;; + esac + + case "$OS" in + darwin|linux) ;; + *) error "Unsupported OS: $OS (use Windows installer for Windows)" ;; + esac +} + +# === Version Resolution === +resolve_version() { + if [ "$VERSION" = "latest" ]; then + info "Fetching latest version..." + VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') + if [ -z "$VERSION" ]; then + error "Failed to fetch latest version from GitHub API" + fi + fi +} + +# === Download Helpers === +url_exists() { + curl -fsSLI "$1" 2>/dev/null | grep -qE "HTTP/.* [23][0-9][0-9]" +} + +find_archive() { + local base="$1" + local variant="$2" + + # Build candidate list (prefer xz over gz, variant over full) + local candidates=() + if [ -n "$variant" ]; then + candidates+=("${base}-${variant}-${OS}-${ARCH}.tar.xz") + candidates+=("${base}-${variant}-${OS}-${ARCH}.tar.gz") + fi + candidates+=("${base}-${OS}-${ARCH}.tar.xz") + candidates+=("${base}-${OS}-${ARCH}.tar.gz") + + for archive in "${candidates[@]}"; do + local url="https://github.com/${REPO}/releases/download/${VERSION}/${archive}" + if url_exists "$url"; then + ARCHIVE="$archive" + DOWNLOAD_URL="$url" + return 0 + fi + done + + error "No compatible archive found for ${OS}/${ARCH}" +} + +download_and_extract() { + TMPDIR=$(mktemp -d) + trap 'rm -rf "$TMPDIR"' EXIT + + info "Downloading ${ARCHIVE}..." + if ! curl -fsSL "$DOWNLOAD_URL" -o "$TMPDIR/$ARCHIVE"; then + error "Failed to download ${DOWNLOAD_URL}" + fi + + info "Extracting..." + case "$ARCHIVE" in + *.tar.xz) tar -xJf "$TMPDIR/$ARCHIVE" -C "$TMPDIR" || error "Failed to extract archive" ;; + *.tar.gz) tar -xzf "$TMPDIR/$ARCHIVE" -C "$TMPDIR" || error "Failed to extract archive" ;; + *) error "Unknown archive format: $ARCHIVE" ;; + esac +} + +install_binary() { + local install_dir="${1:-/usr/local/bin}" + + info "Installing to ${install_dir}..." + if [ -w "$install_dir" ]; then + mv "$TMPDIR/${BINARY}" "${install_dir}/${BINARY}" + else + sudo mv "$TMPDIR/${BINARY}" "${install_dir}/${BINARY}" + fi +} + +verify_install() { + if command -v "$BINARY" &>/dev/null; then + success "Installed successfully!" + dim "$($BINARY --version)" + else + success "Installed to ${1:-/usr/local/bin}/${BINARY}" + dim "Add the directory to your PATH if not already present" + fi +} + +# === Installation Modes === + +install_setup() { + echo -e "${BOLD}Core CLI Installer${NC}" + echo "" + + detect_platform + resolve_version + + local install_dir="/usr/local/bin" + info "Installing ${BINARY} ${VERSION} for ${OS}/${ARCH}..." + find_archive "$BINARY" "" + download_and_extract + install_binary "$install_dir" + verify_install "$install_dir" +} + +install_ci() { + detect_platform + resolve_version + + echo "Installing ${BINARY} ${VERSION} (${OS}/${ARCH})..." + find_archive "$BINARY" "" + download_and_extract + + # CI: prefer /usr/local/bin, no sudo prompts + if [ -w /usr/local/bin ]; then + mv "$TMPDIR/${BINARY}" /usr/local/bin/ + else + sudo mv "$TMPDIR/${BINARY}" /usr/local/bin/ + fi + + ${BINARY} --version +} + +install_dev() { + detect_platform + resolve_version + + local install_dir="/usr/local/bin" + info "Installing ${BINARY} ${VERSION} (full) for ${OS}/${ARCH}..." + find_archive "$BINARY" "" + download_and_extract + install_binary "$install_dir" + verify_install "$install_dir" + + echo "" + echo "Full development variant installed. Available commands:" + echo " core dev - Multi-repo workflows" + echo " core build - Cross-platform builds" + echo " core release - Build and publish releases" +} + +install_variant() { + local variant="$1" + + detect_platform + resolve_version + + local install_dir="/usr/local/bin" + info "Installing ${BINARY} ${VERSION} (${variant} variant) for ${OS}/${ARCH}..." + find_archive "$BINARY" "$variant" + + if [[ "$ARCHIVE" == "${BINARY}-${OS}-${ARCH}"* ]]; then + dim "Using full variant (${variant} variant not available for ${VERSION})" + fi + + download_and_extract + install_binary "$install_dir" + verify_install "$install_dir" +} + +# === Main === +case "$MODE" in + setup) install_setup ;; + ci) install_ci ;; + dev) install_dev ;; + variant) + [ -z "$VARIANT" ] && error "VARIANT must be specified when MODE=variant" + install_variant "$VARIANT" + ;; + *) error "Unknown mode: $MODE" ;; +esac diff --git a/pkg/build/archive.go b/pkg/build/archive.go index b0451f29..3e38bac1 100644 --- a/pkg/build/archive.go +++ b/pkg/build/archive.go @@ -4,19 +4,49 @@ package build import ( "archive/tar" "archive/zip" + "bytes" "compress/gzip" "fmt" "io" "os" "path/filepath" "strings" + + "github.com/Snider/Borg/pkg/compress" ) -// Archive creates an archive for a single artifact. +// ArchiveFormat specifies the compression format for archives. +type ArchiveFormat string + +const ( + // ArchiveFormatGzip uses tar.gz (gzip compression) - widely compatible. + ArchiveFormatGzip ArchiveFormat = "gz" + // ArchiveFormatXZ uses tar.xz (xz/LZMA2 compression) - better compression ratio. + ArchiveFormatXZ ArchiveFormat = "xz" + // ArchiveFormatZip uses zip - for Windows. + ArchiveFormatZip ArchiveFormat = "zip" +) + +// Archive creates an archive for a single artifact using gzip compression. // Uses tar.gz for linux/darwin and zip for windows. // The archive is created alongside the binary (e.g., dist/myapp_linux_amd64.tar.gz). // Returns a new Artifact with Path pointing to the archive. func Archive(artifact Artifact) (Artifact, error) { + return ArchiveWithFormat(artifact, ArchiveFormatGzip) +} + +// ArchiveXZ creates an archive for a single artifact using xz compression. +// Uses tar.xz for linux/darwin and zip for windows. +// Returns a new Artifact with Path pointing to the archive. +func ArchiveXZ(artifact Artifact) (Artifact, error) { + return ArchiveWithFormat(artifact, ArchiveFormatXZ) +} + +// ArchiveWithFormat creates an archive for a single artifact with the specified format. +// Uses tar.gz or tar.xz for linux/darwin and zip for windows. +// The archive is created alongside the binary (e.g., dist/myapp_linux_amd64.tar.xz). +// Returns a new Artifact with Path pointing to the archive. +func ArchiveWithFormat(artifact Artifact, format ArchiveFormat) (Artifact, error) { if artifact.Path == "" { return Artifact{}, fmt.Errorf("build.Archive: artifact path is empty") } @@ -30,7 +60,7 @@ func Archive(artifact Artifact) (Artifact, error) { return Artifact{}, fmt.Errorf("build.Archive: source path is a directory, expected file") } - // Determine archive type based on OS + // Determine archive type based on OS and format var archivePath string var archiveFunc func(src, dst string) error @@ -38,8 +68,14 @@ func Archive(artifact Artifact) (Artifact, error) { archivePath = archiveFilename(artifact, ".zip") archiveFunc = createZipArchive } else { - archivePath = archiveFilename(artifact, ".tar.gz") - archiveFunc = createTarGzArchive + switch format { + case ArchiveFormatXZ: + archivePath = archiveFilename(artifact, ".tar.xz") + archiveFunc = createTarXzArchive + default: + archivePath = archiveFilename(artifact, ".tar.gz") + archiveFunc = createTarGzArchive + } } // Create the archive @@ -55,16 +91,28 @@ func Archive(artifact Artifact) (Artifact, error) { }, nil } -// ArchiveAll archives all artifacts. +// ArchiveAll archives all artifacts using gzip compression. // Returns a slice of new artifacts pointing to the archives. func ArchiveAll(artifacts []Artifact) ([]Artifact, error) { + return ArchiveAllWithFormat(artifacts, ArchiveFormatGzip) +} + +// ArchiveAllXZ archives all artifacts using xz compression. +// Returns a slice of new artifacts pointing to the archives. +func ArchiveAllXZ(artifacts []Artifact) ([]Artifact, error) { + return ArchiveAllWithFormat(artifacts, ArchiveFormatXZ) +} + +// ArchiveAllWithFormat archives all artifacts with the specified format. +// Returns a slice of new artifacts pointing to the archives. +func ArchiveAllWithFormat(artifacts []Artifact, format ArchiveFormat) ([]Artifact, error) { if len(artifacts) == 0 { return nil, nil } var archived []Artifact for _, artifact := range artifacts { - arch, err := Archive(artifact) + arch, err := ArchiveWithFormat(artifact, format) if err != nil { return archived, fmt.Errorf("build.ArchiveAll: failed to archive %s: %w", artifact.Path, err) } @@ -92,6 +140,58 @@ func archiveFilename(artifact Artifact, ext string) string { return filepath.Join(outputDir, archiveName) } +// createTarXzArchive creates a tar.xz archive containing a single file. +// Uses Borg's compress package for xz compression. +func createTarXzArchive(src, dst string) error { + // Open the source file + srcFile, err := os.Open(src) + if err != nil { + return fmt.Errorf("failed to open source file: %w", err) + } + defer srcFile.Close() + + srcInfo, err := srcFile.Stat() + if err != nil { + return fmt.Errorf("failed to stat source file: %w", err) + } + + // Create tar archive in memory + var tarBuf bytes.Buffer + tarWriter := tar.NewWriter(&tarBuf) + + // Create tar header + header, err := tar.FileInfoHeader(srcInfo, "") + if err != nil { + return fmt.Errorf("failed to create tar header: %w", err) + } + header.Name = filepath.Base(src) + + if err := tarWriter.WriteHeader(header); err != nil { + return fmt.Errorf("failed to write tar header: %w", err) + } + + if _, err := io.Copy(tarWriter, srcFile); err != nil { + return fmt.Errorf("failed to write file content to tar: %w", err) + } + + if err := tarWriter.Close(); err != nil { + return fmt.Errorf("failed to close tar writer: %w", err) + } + + // Compress with xz using Borg + xzData, err := compress.Compress(tarBuf.Bytes(), "xz") + if err != nil { + return fmt.Errorf("failed to compress with xz: %w", err) + } + + // Write to destination file + if err := os.WriteFile(dst, xzData, 0644); err != nil { + return fmt.Errorf("failed to write archive file: %w", err) + } + + return nil +} + // createTarGzArchive creates a tar.gz archive containing a single file. func createTarGzArchive(src, dst string) error { // Open the source file diff --git a/pkg/build/archive_test.go b/pkg/build/archive_test.go index 27d66608..0d3a5c7d 100644 --- a/pkg/build/archive_test.go +++ b/pkg/build/archive_test.go @@ -3,12 +3,14 @@ package build import ( "archive/tar" "archive/zip" + "bytes" "compress/gzip" "io" "os" "path/filepath" "testing" + "github.com/Snider/Borg/pkg/compress" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -113,6 +115,64 @@ func TestArchive_Good(t *testing.T) { require.NoError(t, err) assert.Equal(t, "abc123", result.Checksum) }) + + t.Run("creates tar.xz for linux with ArchiveXZ", func(t *testing.T) { + binaryPath, outputDir := setupArchiveTestFile(t, "myapp", "linux", "amd64") + + artifact := Artifact{ + Path: binaryPath, + OS: "linux", + Arch: "amd64", + } + + result, err := ArchiveXZ(artifact) + require.NoError(t, err) + + expectedPath := filepath.Join(outputDir, "myapp_linux_amd64.tar.xz") + assert.Equal(t, expectedPath, result.Path) + assert.FileExists(t, result.Path) + + verifyTarXzContent(t, result.Path, "myapp") + }) + + t.Run("creates tar.xz for darwin with ArchiveWithFormat", func(t *testing.T) { + binaryPath, outputDir := setupArchiveTestFile(t, "myapp", "darwin", "arm64") + + artifact := Artifact{ + Path: binaryPath, + OS: "darwin", + Arch: "arm64", + } + + result, err := ArchiveWithFormat(artifact, ArchiveFormatXZ) + require.NoError(t, err) + + expectedPath := filepath.Join(outputDir, "myapp_darwin_arm64.tar.xz") + assert.Equal(t, expectedPath, result.Path) + assert.FileExists(t, result.Path) + + verifyTarXzContent(t, result.Path, "myapp") + }) + + t.Run("windows still uses zip even with xz format", func(t *testing.T) { + binaryPath, outputDir := setupArchiveTestFile(t, "myapp.exe", "windows", "amd64") + + artifact := Artifact{ + Path: binaryPath, + OS: "windows", + Arch: "amd64", + } + + result, err := ArchiveWithFormat(artifact, ArchiveFormatXZ) + require.NoError(t, err) + + // Windows should still get .zip regardless of format + expectedPath := filepath.Join(outputDir, "myapp_windows_amd64.zip") + assert.Equal(t, expectedPath, result.Path) + assert.FileExists(t, result.Path) + + verifyZipContent(t, result.Path, "myapp.exe") + }) } func TestArchive_Bad(t *testing.T) { @@ -306,3 +366,27 @@ func verifyZipContent(t *testing.T, archivePath, expectedName string) { require.Len(t, reader.File, 1) assert.Equal(t, expectedName, reader.File[0].Name) } + +// verifyTarXzContent opens a tar.xz file and verifies it contains the expected file. +func verifyTarXzContent(t *testing.T, archivePath, expectedName string) { + t.Helper() + + // Read the xz-compressed file + xzData, err := os.ReadFile(archivePath) + require.NoError(t, err) + + // Decompress with Borg + tarData, err := compress.Decompress(xzData) + require.NoError(t, err) + + // Read tar archive + tarReader := tar.NewReader(bytes.NewReader(tarData)) + + header, err := tarReader.Next() + require.NoError(t, err) + assert.Equal(t, expectedName, header.Name) + + // Verify there's only one file + _, err = tarReader.Next() + assert.Equal(t, io.EOF, err) +} diff --git a/tools/install/agent.bat b/tools/install/agent.bat deleted file mode 100644 index 3302d27a..00000000 --- a/tools/install/agent.bat +++ /dev/null @@ -1,72 +0,0 @@ -@echo off -REM Core CLI installer - AI agent variant (Windows) -REM Usage: curl -fsSL https://core.io.in/agent.bat -o agent.bat && agent.bat -setlocal enabledelayedexpansion - -set "VERSION=%~1" -if "%VERSION%"=="" set "VERSION=latest" -set "REPO=host-uk/core" -set "BINARY=core" -set "VARIANT=agent" -set "INSTALL_DIR=%LOCALAPPDATA%\Programs\core" - -if not exist "%INSTALL_DIR%" mkdir "%INSTALL_DIR%" - -if "%VERSION%"=="latest" ( - for /f "tokens=2 delims=:" %%a in ('curl -fsSL "https://api.github.com/repos/%REPO%/releases/latest" ^| findstr "tag_name"') do ( - set "VERSION=%%a" - set "VERSION=!VERSION:"=!" - set "VERSION=!VERSION: =!" - set "VERSION=!VERSION:,=!" - ) - if "!VERSION!"=="" ( - echo ERROR: Failed to fetch latest version - exit /b 1 - ) - if "!VERSION!"=="latest" ( - echo ERROR: Failed to resolve version - exit /b 1 - ) -) - -echo Installing %BINARY% !VERSION! (%VARIANT% variant)... - -set "ARCHIVE=%BINARY%-%VARIANT%-windows-amd64.zip" -set "URL=https://github.com/%REPO%/releases/download/!VERSION!/%ARCHIVE%" - -curl -fsSLI "%URL%" 2>nul | findstr /r "HTTP/.* [23]0[02]" >nul -if errorlevel 1 ( - set "ARCHIVE=%BINARY%-windows-amd64.zip" - echo Using full variant (%VARIANT% variant not available^) -) - -curl -fsSL "https://github.com/%REPO%/releases/download/!VERSION!/!ARCHIVE!" -o "%TEMP%\!ARCHIVE!" -if errorlevel 1 ( - echo ERROR: Failed to download !ARCHIVE! - exit /b 1 -) - -powershell -Command "try { Expand-Archive -Force '%TEMP%\!ARCHIVE!' '%INSTALL_DIR%' } catch { exit 1 }" -if errorlevel 1 ( - echo ERROR: Failed to extract archive - del "%TEMP%\!ARCHIVE!" 2>nul - exit /b 1 -) -del "%TEMP%\!ARCHIVE!" 2>nul - -REM Add to PATH using PowerShell (avoids setx 1024 char limit) -echo %PATH% | findstr /i /c:"%INSTALL_DIR%" >nul -if errorlevel 1 ( - powershell -Command "[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path', 'User') + ';%INSTALL_DIR%', 'User')" - set "PATH=%PATH%;%INSTALL_DIR%" -) - -if not exist "%INSTALL_DIR%\%BINARY%.exe" ( - echo ERROR: Installation failed - binary not found - exit /b 1 -) - -"%INSTALL_DIR%\%BINARY%.exe" --version -if errorlevel 1 exit /b 1 - -endlocal diff --git a/tools/install/agent.sh b/tools/install/agent.sh deleted file mode 100644 index 8d8e5392..00000000 --- a/tools/install/agent.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# Core CLI installer - AI agent variant -# Usage: curl -fsSL https://core.io.in/agent.sh | bash -set -eo pipefail - -VERSION="${VERSION:-${1:-latest}}" -REPO="host-uk/core" -BINARY="core" -VARIANT="agent" - -OS="$(uname -s | tr '[:upper:]' '[:lower:]')" -ARCH="$(uname -m)" -case "$ARCH" in - x86_64|amd64) ARCH="amd64" ;; - arm64|aarch64) ARCH="arm64" ;; - *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; -esac - -if [ "$VERSION" = "latest" ]; then - VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') - if [ -z "$VERSION" ]; then - echo "Failed to fetch latest version from GitHub API" >&2 - exit 1 - fi -fi - -echo "Installing ${BINARY} ${VERSION} (${VARIANT} variant) for ${OS}/${ARCH}..." - -# Download variant-specific archive if available, else fall back to full -ARCHIVE="${BINARY}-${VARIANT}-${OS}-${ARCH}.tar.gz" -URL="https://github.com/${REPO}/releases/download/${VERSION}/${ARCHIVE}" - -if ! curl -fsSLI "$URL" 2>/dev/null | grep -qE "^HTTP/.* (200|302)"; then - ARCHIVE="${BINARY}-${OS}-${ARCH}.tar.gz" - URL="https://github.com/${REPO}/releases/download/${VERSION}/${ARCHIVE}" - echo "Using full variant (${VARIANT} variant not available for ${VERSION})" -fi - -TMPDIR=$(mktemp -d) -trap 'rm -rf "$TMPDIR"' EXIT - -if ! curl -fsSL "$URL" -o "$TMPDIR/$ARCHIVE"; then - echo "Failed to download ${ARCHIVE}" >&2 - exit 1 -fi -tar -xzf "$TMPDIR/$ARCHIVE" -C "$TMPDIR" - -[ -w /usr/local/bin ] && mv "$TMPDIR/${BINARY}" /usr/local/bin/ || sudo mv "$TMPDIR/${BINARY}" /usr/local/bin/ - -${BINARY} --version diff --git a/tools/install/ci.bat b/tools/install/ci.bat deleted file mode 100644 index feb14d01..00000000 --- a/tools/install/ci.bat +++ /dev/null @@ -1,57 +0,0 @@ -@echo off -REM Core CLI installer for Windows CI environments -REM Usage: curl -fsSL https://core.io.in/ci.bat -o ci.bat && ci.bat -setlocal enabledelayedexpansion - -set "VERSION=%~1" -if "%VERSION%"=="" set "VERSION=latest" -set "REPO=host-uk/core" -set "BINARY=core" - -if "%VERSION%"=="latest" ( - for /f "tokens=2 delims=:" %%a in ('curl -fsSL "https://api.github.com/repos/%REPO%/releases/latest" ^| findstr "tag_name"') do ( - set "VERSION=%%a" - set "VERSION=!VERSION:"=!" - set "VERSION=!VERSION: =!" - set "VERSION=!VERSION:,=!" - ) - if "!VERSION!"=="latest" ( - echo ERROR: Failed to fetch latest version from GitHub API - exit /b 1 - ) - if "!VERSION!"=="" ( - echo ERROR: Failed to fetch latest version from GitHub API - exit /b 1 - ) -) - -echo Installing %BINARY% !VERSION!... - -set "ARCHIVE=%BINARY%-windows-amd64.zip" -curl -fsSL "https://github.com/%REPO%/releases/download/!VERSION!/%ARCHIVE%" -o "%TEMP%\%ARCHIVE%" -if errorlevel 1 ( - echo ERROR: Failed to download %ARCHIVE% - exit /b 1 -) - -powershell -Command "try { Expand-Archive -Force '%TEMP%\%ARCHIVE%' '%TEMP%\core-extract' } catch { exit 1 }" -if errorlevel 1 ( - echo ERROR: Failed to extract archive - del "%TEMP%\%ARCHIVE%" 2>nul - exit /b 1 -) - -REM Try System32 first (CI runners often have admin), else use local programs -move /y "%TEMP%\core-extract\%BINARY%.exe" "C:\Windows\System32\%BINARY%.exe" >nul 2>&1 -if errorlevel 1 ( - if not exist "%LOCALAPPDATA%\Programs" mkdir "%LOCALAPPDATA%\Programs" - move /y "%TEMP%\core-extract\%BINARY%.exe" "%LOCALAPPDATA%\Programs\%BINARY%.exe" - set "PATH=%LOCALAPPDATA%\Programs;%PATH%" - echo NOTE: Installed to %LOCALAPPDATA%\Programs -) -rmdir /s /q "%TEMP%\core-extract" 2>nul -del "%TEMP%\%ARCHIVE%" 2>nul - -%BINARY% --version || exit /b 1 - -endlocal diff --git a/tools/install/ci.sh b/tools/install/ci.sh deleted file mode 100644 index 99d2d89f..00000000 --- a/tools/install/ci.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# Core CLI installer for CI environments -# Minimal, fast, no interactive prompts -# Usage: curl -fsSL https://core.io.in/ci.sh | bash -# VERSION=v1.0.0 curl -fsSL https://core.io.in/ci.sh | bash -set -eo pipefail - -VERSION="${VERSION:-${1:-latest}}" -REPO="host-uk/core" -BINARY="core" - -# Detect platform -OS="$(uname -s | tr '[:upper:]' '[:lower:]')" -ARCH="$(uname -m)" -case "$ARCH" in - x86_64|amd64) ARCH="amd64" ;; - arm64|aarch64) ARCH="arm64" ;; - *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; -esac - -# Resolve latest -if [ "$VERSION" = "latest" ]; then - VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') - if [ -z "$VERSION" ]; then - echo "Failed to fetch latest version from GitHub API" >&2 - exit 1 - fi -fi - -echo "Installing ${BINARY} ${VERSION} (${OS}/${ARCH})..." - -# Download and extract to secure temp dir -ARCHIVE="${BINARY}-${OS}-${ARCH}.tar.gz" -TMPDIR=$(mktemp -d) -trap 'rm -rf "$TMPDIR"' EXIT - -if ! curl -fsSL "https://github.com/${REPO}/releases/download/${VERSION}/${ARCHIVE}" -o "$TMPDIR/$ARCHIVE"; then - echo "Failed to download ${ARCHIVE}" >&2 - exit 1 -fi -tar -xzf "$TMPDIR/$ARCHIVE" -C "$TMPDIR" - -# Install (CI runners typically have write access to /usr/local/bin) -if [ -w /usr/local/bin ]; then - mv "$TMPDIR/${BINARY}" /usr/local/bin/ -else - sudo mv "$TMPDIR/${BINARY}" /usr/local/bin/ -fi - -${BINARY} --version diff --git a/tools/install/dev.bat b/tools/install/dev.bat deleted file mode 100644 index 97e8fe0b..00000000 --- a/tools/install/dev.bat +++ /dev/null @@ -1,69 +0,0 @@ -@echo off -REM Core CLI installer - Multi-repo development variant (Windows) -REM Usage: curl -fsSL https://core.io.in/dev.bat -o dev.bat && dev.bat -setlocal enabledelayedexpansion - -set "VERSION=%~1" -if "%VERSION%"=="" set "VERSION=latest" -set "REPO=host-uk/core" -set "BINARY=core" -set "INSTALL_DIR=%LOCALAPPDATA%\Programs\core" - -if not exist "%INSTALL_DIR%" mkdir "%INSTALL_DIR%" - -if "%VERSION%"=="latest" ( - for /f "tokens=2 delims=:" %%a in ('curl -fsSL "https://api.github.com/repos/%REPO%/releases/latest" ^| findstr "tag_name"') do ( - set "VERSION=%%a" - set "VERSION=!VERSION:"=!" - set "VERSION=!VERSION: =!" - set "VERSION=!VERSION:,=!" - ) - if "!VERSION!"=="" ( - echo ERROR: Failed to fetch latest version - exit /b 1 - ) - if "!VERSION!"=="latest" ( - echo ERROR: Failed to resolve version - exit /b 1 - ) -) - -echo Installing %BINARY% !VERSION! (full) for Windows... - -set "ARCHIVE=%BINARY%-windows-amd64.zip" -curl -fsSL "https://github.com/%REPO%/releases/download/!VERSION!/%ARCHIVE%" -o "%TEMP%\%ARCHIVE%" -if errorlevel 1 ( - echo ERROR: Failed to download %ARCHIVE% - exit /b 1 -) - -powershell -Command "try { Expand-Archive -Force '%TEMP%\%ARCHIVE%' '%INSTALL_DIR%' } catch { exit 1 }" -if errorlevel 1 ( - echo ERROR: Failed to extract archive - del "%TEMP%\%ARCHIVE%" 2>nul - exit /b 1 -) -del "%TEMP%\%ARCHIVE%" 2>nul - -REM Add to PATH using PowerShell (avoids setx 1024 char limit) -echo %PATH% | findstr /i /c:"%INSTALL_DIR%" >nul -if errorlevel 1 ( - powershell -Command "[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path', 'User') + ';%INSTALL_DIR%', 'User')" - set "PATH=%PATH%;%INSTALL_DIR%" -) - -if not exist "%INSTALL_DIR%\%BINARY%.exe" ( - echo ERROR: Installation failed - binary not found - exit /b 1 -) - -"%INSTALL_DIR%\%BINARY%.exe" --version -if errorlevel 1 exit /b 1 - -echo. -echo Full development variant installed. Available commands: -echo core dev - Multi-repo workflows -echo core build - Cross-platform builds -echo core release - Build and publish releases - -endlocal diff --git a/tools/install/dev.sh b/tools/install/dev.sh deleted file mode 100644 index 4d01bdc8..00000000 --- a/tools/install/dev.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -# Core CLI installer - Multi-repo development variant (full) -# Usage: curl -fsSL https://core.io.in/dev.sh | bash -set -eo pipefail - -VERSION="${VERSION:-${1:-latest}}" -REPO="host-uk/core" -BINARY="core" - -OS="$(uname -s | tr '[:upper:]' '[:lower:]')" -ARCH="$(uname -m)" -case "$ARCH" in - x86_64|amd64) ARCH="amd64" ;; - arm64|aarch64) ARCH="arm64" ;; - *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; -esac - -if [ "$VERSION" = "latest" ]; then - VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') - if [ -z "$VERSION" ]; then - echo "Failed to fetch latest version from GitHub API" >&2 - exit 1 - fi -fi - -echo "Installing ${BINARY} ${VERSION} (full) for ${OS}/${ARCH}..." - -ARCHIVE="${BINARY}-${OS}-${ARCH}.tar.gz" -TMPDIR=$(mktemp -d) -trap 'rm -rf "$TMPDIR"' EXIT - -if ! curl -fsSL "https://github.com/${REPO}/releases/download/${VERSION}/${ARCHIVE}" -o "$TMPDIR/$ARCHIVE"; then - echo "Failed to download ${ARCHIVE}" >&2 - exit 1 -fi -tar -xzf "$TMPDIR/$ARCHIVE" -C "$TMPDIR" - -[ -w /usr/local/bin ] && mv "$TMPDIR/${BINARY}" /usr/local/bin/ || sudo mv "$TMPDIR/${BINARY}" /usr/local/bin/ - -${BINARY} --version - -echo "" -echo "Full development variant installed. Available commands:" -echo " core dev - Multi-repo workflows" -echo " core build - Cross-platform builds" -echo " core release - Build and publish releases" diff --git a/tools/install/go.bat b/tools/install/go.bat deleted file mode 100644 index c063fd3e..00000000 --- a/tools/install/go.bat +++ /dev/null @@ -1,72 +0,0 @@ -@echo off -REM Core CLI installer - Go development variant (Windows) -REM Usage: curl -fsSL https://core.io.in/go.bat -o go.bat && go.bat -setlocal enabledelayedexpansion - -set "VERSION=%~1" -if "%VERSION%"=="" set "VERSION=latest" -set "REPO=host-uk/core" -set "BINARY=core" -set "VARIANT=go" -set "INSTALL_DIR=%LOCALAPPDATA%\Programs\core" - -if not exist "%INSTALL_DIR%" mkdir "%INSTALL_DIR%" - -if "%VERSION%"=="latest" ( - for /f "tokens=2 delims=:" %%a in ('curl -fsSL "https://api.github.com/repos/%REPO%/releases/latest" ^| findstr "tag_name"') do ( - set "VERSION=%%a" - set "VERSION=!VERSION:"=!" - set "VERSION=!VERSION: =!" - set "VERSION=!VERSION:,=!" - ) - if "!VERSION!"=="" ( - echo ERROR: Failed to fetch latest version - exit /b 1 - ) - if "!VERSION!"=="latest" ( - echo ERROR: Failed to resolve version - exit /b 1 - ) -) - -echo Installing %BINARY% !VERSION! (%VARIANT% variant)... - -set "ARCHIVE=%BINARY%-%VARIANT%-windows-amd64.zip" -set "URL=https://github.com/%REPO%/releases/download/!VERSION!/%ARCHIVE%" - -curl -fsSLI "%URL%" 2>nul | findstr /r "HTTP/.* [23]0[02]" >nul -if errorlevel 1 ( - set "ARCHIVE=%BINARY%-windows-amd64.zip" - echo Using full variant (%VARIANT% variant not available^) -) - -curl -fsSL "https://github.com/%REPO%/releases/download/!VERSION!/!ARCHIVE!" -o "%TEMP%\!ARCHIVE!" -if errorlevel 1 ( - echo ERROR: Failed to download !ARCHIVE! - exit /b 1 -) - -powershell -Command "try { Expand-Archive -Force '%TEMP%\!ARCHIVE!' '%INSTALL_DIR%' } catch { exit 1 }" -if errorlevel 1 ( - echo ERROR: Failed to extract archive - del "%TEMP%\!ARCHIVE!" 2>nul - exit /b 1 -) -del "%TEMP%\!ARCHIVE!" 2>nul - -REM Add to PATH using PowerShell (avoids setx 1024 char limit) -echo %PATH% | findstr /i /c:"%INSTALL_DIR%" >nul -if errorlevel 1 ( - powershell -Command "[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path', 'User') + ';%INSTALL_DIR%', 'User')" - set "PATH=%PATH%;%INSTALL_DIR%" -) - -if not exist "%INSTALL_DIR%\%BINARY%.exe" ( - echo ERROR: Installation failed - binary not found - exit /b 1 -) - -"%INSTALL_DIR%\%BINARY%.exe" --version -if errorlevel 1 exit /b 1 - -endlocal diff --git a/tools/install/go.sh b/tools/install/go.sh deleted file mode 100644 index 8bfca7f5..00000000 --- a/tools/install/go.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# Core CLI installer - Go development variant -# Usage: curl -fsSL https://core.io.in/go.sh | bash -set -eo pipefail - -VERSION="${VERSION:-${1:-latest}}" -REPO="host-uk/core" -BINARY="core" -VARIANT="go" - -OS="$(uname -s | tr '[:upper:]' '[:lower:]')" -ARCH="$(uname -m)" -case "$ARCH" in - x86_64|amd64) ARCH="amd64" ;; - arm64|aarch64) ARCH="arm64" ;; - *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; -esac - -if [ "$VERSION" = "latest" ]; then - VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') - if [ -z "$VERSION" ]; then - echo "Failed to fetch latest version from GitHub API" >&2 - exit 1 - fi -fi - -echo "Installing ${BINARY} ${VERSION} (${VARIANT} variant) for ${OS}/${ARCH}..." - -# Download variant-specific archive if available, else fall back to full -ARCHIVE="${BINARY}-${VARIANT}-${OS}-${ARCH}.tar.gz" -URL="https://github.com/${REPO}/releases/download/${VERSION}/${ARCHIVE}" - -if ! curl -fsSLI "$URL" 2>/dev/null | grep -qE "^HTTP/.* (200|302)"; then - # Fall back to full variant - ARCHIVE="${BINARY}-${OS}-${ARCH}.tar.gz" - URL="https://github.com/${REPO}/releases/download/${VERSION}/${ARCHIVE}" - echo "Using full variant (${VARIANT} variant not available for ${VERSION})" -fi - -TMPDIR=$(mktemp -d) -trap 'rm -rf "$TMPDIR"' EXIT - -if ! curl -fsSL "$URL" -o "$TMPDIR/$ARCHIVE"; then - echo "Failed to download ${ARCHIVE}" >&2 - exit 1 -fi -tar -xzf "$TMPDIR/$ARCHIVE" -C "$TMPDIR" - -[ -w /usr/local/bin ] && mv "$TMPDIR/${BINARY}" /usr/local/bin/ || sudo mv "$TMPDIR/${BINARY}" /usr/local/bin/ - -${BINARY} --version diff --git a/tools/install/php.bat b/tools/install/php.bat deleted file mode 100644 index 20a021cb..00000000 --- a/tools/install/php.bat +++ /dev/null @@ -1,72 +0,0 @@ -@echo off -REM Core CLI installer - PHP/Laravel development variant (Windows) -REM Usage: curl -fsSL https://core.io.in/php.bat -o php.bat && php.bat -setlocal enabledelayedexpansion - -set "VERSION=%~1" -if "%VERSION%"=="" set "VERSION=latest" -set "REPO=host-uk/core" -set "BINARY=core" -set "VARIANT=php" -set "INSTALL_DIR=%LOCALAPPDATA%\Programs\core" - -if not exist "%INSTALL_DIR%" mkdir "%INSTALL_DIR%" - -if "%VERSION%"=="latest" ( - for /f "tokens=2 delims=:" %%a in ('curl -fsSL "https://api.github.com/repos/%REPO%/releases/latest" ^| findstr "tag_name"') do ( - set "VERSION=%%a" - set "VERSION=!VERSION:"=!" - set "VERSION=!VERSION: =!" - set "VERSION=!VERSION:,=!" - ) - if "!VERSION!"=="" ( - echo ERROR: Failed to fetch latest version - exit /b 1 - ) - if "!VERSION!"=="latest" ( - echo ERROR: Failed to resolve version - exit /b 1 - ) -) - -echo Installing %BINARY% !VERSION! (%VARIANT% variant)... - -set "ARCHIVE=%BINARY%-%VARIANT%-windows-amd64.zip" -set "URL=https://github.com/%REPO%/releases/download/!VERSION!/%ARCHIVE%" - -curl -fsSLI "%URL%" 2>nul | findstr /r "HTTP/.* [23]0[02]" >nul -if errorlevel 1 ( - set "ARCHIVE=%BINARY%-windows-amd64.zip" - echo Using full variant (%VARIANT% variant not available^) -) - -curl -fsSL "https://github.com/%REPO%/releases/download/!VERSION!/!ARCHIVE!" -o "%TEMP%\!ARCHIVE!" -if errorlevel 1 ( - echo ERROR: Failed to download !ARCHIVE! - exit /b 1 -) - -powershell -Command "try { Expand-Archive -Force '%TEMP%\!ARCHIVE!' '%INSTALL_DIR%' } catch { exit 1 }" -if errorlevel 1 ( - echo ERROR: Failed to extract archive - del "%TEMP%\!ARCHIVE!" 2>nul - exit /b 1 -) -del "%TEMP%\!ARCHIVE!" 2>nul - -REM Add to PATH using PowerShell (avoids setx 1024 char limit) -echo %PATH% | findstr /i /c:"%INSTALL_DIR%" >nul -if errorlevel 1 ( - powershell -Command "[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path', 'User') + ';%INSTALL_DIR%', 'User')" - set "PATH=%PATH%;%INSTALL_DIR%" -) - -if not exist "%INSTALL_DIR%\%BINARY%.exe" ( - echo ERROR: Installation failed - binary not found - exit /b 1 -) - -"%INSTALL_DIR%\%BINARY%.exe" --version -if errorlevel 1 exit /b 1 - -endlocal diff --git a/tools/install/php.sh b/tools/install/php.sh deleted file mode 100644 index 46a2f40e..00000000 --- a/tools/install/php.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# Core CLI installer - PHP/Laravel development variant -# Usage: curl -fsSL https://core.io.in/php.sh | bash -set -eo pipefail - -VERSION="${VERSION:-${1:-latest}}" -REPO="host-uk/core" -BINARY="core" -VARIANT="php" - -OS="$(uname -s | tr '[:upper:]' '[:lower:]')" -ARCH="$(uname -m)" -case "$ARCH" in - x86_64|amd64) ARCH="amd64" ;; - arm64|aarch64) ARCH="arm64" ;; - *) echo "Unsupported architecture: $ARCH" >&2; exit 1 ;; -esac - -if [ "$VERSION" = "latest" ]; then - VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') - if [ -z "$VERSION" ]; then - echo "Failed to fetch latest version from GitHub API" >&2 - exit 1 - fi -fi - -echo "Installing ${BINARY} ${VERSION} (${VARIANT} variant) for ${OS}/${ARCH}..." - -# Download variant-specific archive if available, else fall back to full -ARCHIVE="${BINARY}-${VARIANT}-${OS}-${ARCH}.tar.gz" -URL="https://github.com/${REPO}/releases/download/${VERSION}/${ARCHIVE}" - -if ! curl -fsSLI "$URL" 2>/dev/null | grep -qE "^HTTP/.* (200|302)"; then - ARCHIVE="${BINARY}-${OS}-${ARCH}.tar.gz" - URL="https://github.com/${REPO}/releases/download/${VERSION}/${ARCHIVE}" - echo "Using full variant (${VARIANT} variant not available for ${VERSION})" -fi - -TMPDIR=$(mktemp -d) -trap 'rm -rf "$TMPDIR"' EXIT - -if ! curl -fsSL "$URL" -o "$TMPDIR/$ARCHIVE"; then - echo "Failed to download ${ARCHIVE}" >&2 - exit 1 -fi -tar -xzf "$TMPDIR/$ARCHIVE" -C "$TMPDIR" - -[ -w /usr/local/bin ] && mv "$TMPDIR/${BINARY}" /usr/local/bin/ || sudo mv "$TMPDIR/${BINARY}" /usr/local/bin/ - -${BINARY} --version diff --git a/tools/install/setup.bat b/tools/install/setup.bat deleted file mode 100644 index bfb0401f..00000000 --- a/tools/install/setup.bat +++ /dev/null @@ -1,85 +0,0 @@ -@echo off -REM Core CLI installer for Windows -REM Usage: curl -fsSL https://core.io.in/setup.bat -o setup.bat && setup.bat - -setlocal enabledelayedexpansion - -set "VERSION=%~1" -if "%VERSION%"=="" set "VERSION=latest" -set "REPO=host-uk/core" -set "BINARY=core" -set "INSTALL_DIR=%LOCALAPPDATA%\Programs\core" - -echo [94m>>>[0m Installing Core CLI for Windows... - -REM Create install directory -if not exist "%INSTALL_DIR%" mkdir "%INSTALL_DIR%" - -REM Resolve latest version if needed -if "%VERSION%"=="latest" ( - echo [94m>>>[0m Fetching latest version... - for /f "tokens=2 delims=:" %%a in ('curl -fsSL "https://api.github.com/repos/%REPO%/releases/latest" ^| findstr "tag_name"') do ( - set "VERSION=%%a" - set "VERSION=!VERSION:"=!" - set "VERSION=!VERSION: =!" - set "VERSION=!VERSION:,=!" - ) - if "!VERSION!"=="" ( - echo [91m>>>[0m Failed to fetch latest version - exit /b 1 - ) - if "!VERSION!"=="latest" ( - echo [91m>>>[0m Failed to resolve version - exit /b 1 - ) -) - -echo [94m>>>[0m Installing %BINARY% !VERSION!... - -REM Download archive -set "ARCHIVE=%BINARY%-windows-amd64.zip" -set "DOWNLOAD_URL=https://github.com/%REPO%/releases/download/!VERSION!/%ARCHIVE%" -set "TMP_FILE=%TEMP%\%ARCHIVE%" - -echo [94m>>>[0m Downloading %ARCHIVE%... -curl -fsSL "%DOWNLOAD_URL%" -o "%TMP_FILE%" -if errorlevel 1 ( - echo [91m>>>[0m Failed to download %DOWNLOAD_URL% - exit /b 1 -) - -REM Extract -echo [94m>>>[0m Extracting... -powershell -Command "try { Expand-Archive -Force '%TMP_FILE%' '%INSTALL_DIR%' } catch { exit 1 }" -if errorlevel 1 ( - echo [91m>>>[0m Failed to extract archive - del "%TMP_FILE%" 2>nul - exit /b 1 -) -del "%TMP_FILE%" 2>nul - -REM Add to PATH using PowerShell (avoids setx 1024 char limit) -echo %PATH% | findstr /i /c:"%INSTALL_DIR%" >nul -if errorlevel 1 ( - echo [94m>>>[0m Adding to PATH... - powershell -Command "[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path', 'User') + ';%INSTALL_DIR%', 'User')" - set "PATH=%PATH%;%INSTALL_DIR%" -) - -REM Verify -if not exist "%INSTALL_DIR%\%BINARY%.exe" ( - echo [91m>>>[0m Installation failed - binary not found - exit /b 1 -) - -"%INSTALL_DIR%\%BINARY%.exe" --version -if errorlevel 1 ( - echo [91m>>>[0m Installation verification failed - exit /b 1 -) - -echo [92m>>>[0m Installed successfully! -echo. -echo [90mRestart your terminal to use '%BINARY%' command[0m - -endlocal diff --git a/tools/install/setup.sh b/tools/install/setup.sh deleted file mode 100644 index a90b5126..00000000 --- a/tools/install/setup.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash -# Core CLI installer for macOS and Linux -# Usage: curl -fsSL https://core.io.in/setup.sh | bash -# curl -fsSL https://core.io.in/setup.sh | bash -s -- v1.0.0 -set -eo pipefail - -VERSION="${1:-latest}" -REPO="host-uk/core" -BINARY="core" -INSTALL_DIR="${CORE_INSTALL_DIR:-/usr/local/bin}" - -# Colours -RED='\033[0;31m' -GREEN='\033[0;32m' -BLUE='\033[0;34m' -DIM='\033[2m' -NC='\033[0m' - -info() { echo -e "${BLUE}>>>${NC} $1"; } -success() { echo -e "${GREEN}>>>${NC} $1"; } -error() { echo -e "${RED}>>>${NC} $1" >&2; exit 1; } - -# Detect OS and architecture -OS="$(uname -s | tr '[:upper:]' '[:lower:]')" -ARCH="$(uname -m)" - -case "$ARCH" in - x86_64|amd64) ARCH="amd64" ;; - arm64|aarch64) ARCH="arm64" ;; - *) error "Unsupported architecture: $ARCH" ;; -esac - -case "$OS" in - darwin|linux) ;; - *) error "Unsupported OS: $OS (use setup.bat for Windows)" ;; -esac - -# Resolve latest version -if [ "$VERSION" = "latest" ]; then - info "Fetching latest version..." - VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') - if [ -z "$VERSION" ]; then - error "Failed to fetch latest version" - fi -fi - -info "Installing ${BINARY} ${VERSION} for ${OS}/${ARCH}..." - -# Download archive -ARCHIVE="${BINARY}-${OS}-${ARCH}.tar.gz" -DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${VERSION}/${ARCHIVE}" -TMP_DIR=$(mktemp -d) -trap 'rm -rf "$TMP_DIR"' EXIT - -info "Downloading ${ARCHIVE}..." -if ! curl -fsSL "$DOWNLOAD_URL" -o "${TMP_DIR}/${ARCHIVE}"; then - error "Failed to download ${DOWNLOAD_URL}" -fi - -# Extract -info "Extracting..." -tar -xzf "${TMP_DIR}/${ARCHIVE}" -C "$TMP_DIR" -chmod +x "${TMP_DIR}/${BINARY}" - -# Install -info "Installing to ${INSTALL_DIR}..." -if [ -w "$INSTALL_DIR" ]; then - mv "${TMP_DIR}/${BINARY}" "${INSTALL_DIR}/${BINARY}" -else - sudo mv "${TMP_DIR}/${BINARY}" "${INSTALL_DIR}/${BINARY}" -fi - -# Verify -if command -v "$BINARY" &>/dev/null; then - success "Installed successfully!" - echo -e "${DIM}$($BINARY --version)${NC}" -else - success "Installed to ${INSTALL_DIR}/${BINARY}" - echo -e "${DIM}Add ${INSTALL_DIR} to your PATH if not already present${NC}" -fi