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 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-02-03 07:00:52 +00:00
parent cb5f7030d6
commit e084ccbd51
18 changed files with 595 additions and 760 deletions

1
go.mod
View file

@ -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

2
go.sum
View file

@ -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=

180
install.bat Normal file
View file

@ -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

222
install.sh Normal file
View file

@ -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

View file

@ -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

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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