Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9461fc7
feature: make a script that is easier to do one-line install across …
sofusalbertsen Sep 25, 2025
15711dc
Fix download URL handling for Windows and Linux in install script
sofusalbertsen Sep 25, 2025
9314bbb
Improve error handling for download failures in install script
sofusalbertsen Sep 25, 2025
6d88f76
refactor: simplify Windows script execution in install tests
sofusalbertsen Sep 26, 2025
b08d045
fix: correct file extension from .yaml to .yml in workflow paths
sofusalbertsen Sep 26, 2025
aa358f6
Merge branch 'main' of github.com:kosli-dev/cli into install-script
sofusalbertsen Sep 28, 2025
383a4dc
feat: enable manual triggering of install script tests
sofusalbertsen Sep 29, 2025
1767d69
fix: improve error handling in install script
sofusalbertsen Sep 30, 2025
e090e3e
feat: add debug mode and improve version detection in install script
sofusalbertsen Oct 2, 2025
9e9c0c2
fix: improve OS and architecture detection in install script
sofusalbertsen Oct 2, 2025
0e4a8d5
Merge branch 'main' of github.com:kosli-dev/cli into install-script
sofusalbertsen Oct 2, 2025
b9b6390
Tidy up description of steps in the CI job
sofusalbertsen Oct 2, 2025
bc83abd
fix: correct typo in install script step and enable debug mode
sofusalbertsen Oct 2, 2025
2e99e89
fix: enhance GitHub API response handling for latest version detection
sofusalbertsen Oct 3, 2025
45d397c
fix: improve retry mechanism for fetching latest version from GitHub API
sofusalbertsen Oct 3, 2025
810d622
fix: add GitHub token support to install script for authenticated API…
sofusalbertsen Oct 6, 2025
7848564
fix: update installation instructions for Kosli CLI to include script…
sofusalbertsen Oct 6, 2025
512e287
Merge remote-tracking branch 'origin/main' into install-script
sofusalbertsen Oct 6, 2025
d58d72c
fix: update wording in installation instructions for clarity
sofusalbertsen Oct 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/install-script-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Install Script Tests

on:
push:
paths:
- 'install-cli.sh'
- '.github/workflows/install-script-tests.yml'
pull_request:
paths:
- 'install-cli.sh'
- '.github/workflows/install-script-tests.yml'
workflow_dispatch:

jobs:
test-script:
name: Test Bash Script on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

steps:
- name: Checkout repository
uses: actions/checkout@v5

- name: Run install script
shell: bash
run: bash install-cli.sh --debug --token ${{ secrets.GITHUB_TOKEN }}

- name: Verify installation
shell: bash
run: kosli version
35 changes: 22 additions & 13 deletions docs.kosli.com/content/getting_started/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,28 @@ Kosli CLI can be installed from package managers,
by Curling pre-built binaries, or can be used from the distributed Docker images.
{{< tabs "installKosli" >}}

{{< tab "Script" >}}
You can download the correct Kosli CLI for your platform, given that you can run shell scripts on it, by invoking this one-line script:

```shell {.command}
curl -fL https://raw.githubusercontent.com/kosli-dev/cli/refs/heads/main/install-cli.sh | sh
```
{{< /tab >}}

{{< tab "Docker" >}}
You can run the Kosli CLI with docker:
```shell {.command}
docker run --rm ghcr.io/kosli-dev/cli:v{{< cli-version >}}
```
The `entrypoint` for this container is the kosli command.

To run any kosli command you append it to the `docker run` command above –
without the `kosli` keyword. For example to run `kosli version`:
```shell {.command}
docker run --rm ghcr.io/kosli-dev/cli:v{{< cli-version >}} version
```
{{< /tab >}}

{{< tab "Homebrew" >}}
If you have [Homebrew](https://brew.sh/) (available on MacOS, Linux or Windows Subsystem for Linux),
you can install the Kosli CLI by running:
Expand Down Expand Up @@ -64,20 +86,7 @@ For example, on Mac with AMD:
curl -L https://github.com/kosli-dev/cli/releases/download/v{{< cli-version >}}/kosli_{{< cli-version >}}_darwin_amd64.tar.gz | tar zx
sudo mv kosli /usr/local/bin/kosli
```
{{< /tab >}}

{{< tab "Docker" >}}
You can run the Kosli CLI with docker:
```shell {.command}
docker run --rm ghcr.io/kosli-dev/cli:v{{< cli-version >}}
```
The `entrypoint` for this container is the kosli command.

To run any kosli command you append it to the `docker run` command above –
without the `kosli` keyword. For example to run `kosli version`:
```shell {.command}
docker run --rm ghcr.io/kosli-dev/cli:v{{< cli-version >}} version
```
{{< /tab >}}

{{< tab "From source" >}}
Expand Down
219 changes: 219 additions & 0 deletions install-cli.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
#!/bin/sh
set -eu

# This script downloads the OS- and architecture-specific Kosli CLI binary,
# extracts it, and moves the executable to a directory in your PATH.

# --- Configuration ---
CLI_OS="unknown"
ARCH="unknown"
VERSION=""
FILE_NAME="kosli"
DEBUG=false
GITHUB_TOKEN=""

# --- Debug function ---
debug_print() {
if [ "$DEBUG" = true ]; then
echo "DEBUG: $1" >&2
fi
}

# --- Parse arguments ---
while [ $# -gt 0 ]; do
case $1 in
--debug)
DEBUG=true
debug_print "Debug mode enabled"
shift
;;
--token)
if [ -n "${2:-}" ]; then
GITHUB_TOKEN="$2"
debug_print "GitHub token provided"
shift 2
else
echo "Error: --token requires a value"
exit 1
fi
;;
*)
VERSION=$1
debug_print "Version specified: $VERSION"
shift
;;
esac
done

# --- Version Selection ---
if [ -n "$VERSION" ]; then
echo "Downloading specified version $VERSION of Kosli CLI..."
debug_print "Using specified version: $VERSION"
else
echo "Detecting the latest version of Kosli CLI..."
debug_print "Fetching latest version from GitHub API"

# Retry mechanism for fetching the latest version
RETRY_COUNT=0
MAX_RETRIES=5
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if [ -n "$GITHUB_TOKEN" ]; then
debug_print "Using GitHub token for API request"
METADATA=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/kosli-dev/cli/releases/latest")
else
debug_print "Using unauthenticated API request"
METADATA=$(curl -s "https://api.github.com/repos/kosli-dev/cli/releases/latest")
fi
debug_print "GitHub API response: $METADATA"

# Check if the response contains the expected tag_name
if echo "$METADATA" | grep -q '"tag_name":'; then
TAG_NAME=$(echo "$METADATA" | grep '"tag_name":')
debug_print "GitHub API response tag: $TAG_NAME"
LATEST_TAG=$(echo "$TAG_NAME" | sed -E 's/.*"([^"]+)".*/\1/')
debug_print "GitHub API response tag: $LATEST_TAG"
if [ -z "$LATEST_TAG" ]; then
echo "Error: Could not fetch the latest version tag from GitHub."
exit 1
fi
VERSION=$LATEST_TAG
echo "Latest version is $VERSION. Downloading..."
debug_print "Set VERSION to: $VERSION"
break
else
echo "Warning: GitHub API response did not contain a valid tag_name. Retrying in 5 seconds..."
sleep 5
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
echo "Error: GitHub rate limit exceeded too many times."
exit 1
fi
fi
done
fi
echo ""

# Strip the 'v' prefix for use in the filename, e.g., v2.11.22 -> 2.11.22
VERSION_FILENAME=$(echo "$VERSION" | sed 's/^v//')
debug_print "VERSION_FILENAME after stripping 'v': $VERSION_FILENAME"

# --- OS and Architecture Detection ---
debug_print "Detecting OS and architecture"
debug_print "uname -s output: $(uname -s)"
debug_print "uname -m output: $(uname -m)"

UNAME_S=$(uname -s)
if echo "$UNAME_S" | grep -q -E -i "(cygwin|mingw|msys|windows)"; then
CLI_OS="windows"
ARCH="amd64"
FILE_NAME="${FILE_NAME}.exe"
debug_print "Detected Windows OS"
elif echo "$UNAME_S" | grep -q -i "darwin"; then
CLI_OS="darwin"
debug_print "Detected Darwin/macOS"
UNAME_M=$(uname -m)
if [ "$UNAME_M" = "arm64" ]; then
ARCH="arm64"
debug_print "Detected ARM64 architecture"
else
ARCH="amd64"
debug_print "Detected AMD64 architecture"
fi
else
CLI_OS="linux"
debug_print "Detected Linux OS"
MACHINE_TYPE="$(uname -m)"
debug_print "Machine type: $MACHINE_TYPE"
case $MACHINE_TYPE in
amd64 | x86_64 | x64)
ARCH="amd64"
debug_print "Mapped to AMD64 architecture"
;;
aarch64 | arm64)
ARCH="arm64"
debug_print "Mapped to ARM64 architecture"
;;
*)
echo "Error: Unsupported Linux architecture: $MACHINE_TYPE"
echo "Kosli CLI is only available for amd64 and arm64 on Linux."
exit 1
;;
esac
fi

debug_print "Final values - CLI_OS: $CLI_OS, ARCH: $ARCH, FILE_NAME: $FILE_NAME"

# --- Download and Extract ---
# The download is a .tar.gz or .zip file which needs to be extracted
if [ "$CLI_OS" = "windows" ]; then
URL="https://github.com/kosli-dev/cli/releases/download/${VERSION}/kosli_${VERSION_FILENAME}_${CLI_OS}_${ARCH}.zip"
debug_print "Windows URL constructed: $URL"
echo "Downloading from: $URL"
# Download and extract for Windows
debug_print "Starting Windows download and extraction"
if ! curl -L --fail "$URL" -o kosli.zip; then
echo "Error: Download failed. Please check the URL and your network connection."
exit 1
fi
debug_print "Download completed, extracting zip file"
unzip -o kosli.zip
debug_print "Extraction completed"
else
URL="https://github.com/kosli-dev/cli/releases/download/${VERSION}/kosli_${VERSION_FILENAME}_${CLI_OS}_${ARCH}.tar.gz"
debug_print "Unix URL constructed: $URL"
echo "Downloading from: $URL"
# Download and extract for Linux and Darwin
debug_print "Starting Unix download and extraction"
if ! curl -L --fail "$URL" | tar zx; then
echo "Error: Download or extraction failed. Please check the URL and your network connection."
exit 1
fi
debug_print "Download and extraction completed"
fi

# --- Installation ---
# Move the extracted binary to a directory in the user's PATH
echo "Installing Kosli CLI..."
debug_print "Starting installation process"
debug_print "Current PATH: $PATH"

# Check directories one by one instead of using set --
for dir in "/usr/local/bin" "/usr/bin" "/opt/bin"; do
debug_print "Checking directory: $dir"
# Check if destination directory exists and is in the PATH
if [ -d "$dir" ] && echo "$PATH" | grep -q "$dir"; then
debug_print "Directory $dir exists and is in PATH"
debug_print "Attempting to move $FILE_NAME to $dir"
if mv "$FILE_NAME" "$dir/"; then
echo ""
echo "✅ Kosli CLI was successfully installed in $dir"
echo "Running 'kosli version' to verify:"
debug_print "Installation successful, running version check"
kosli version
exit 0
else
echo ""
echo "Attempting to install with sudo..."
echo "We'd like to install the Kosli CLI executable in '$dir'. Please enter your password if prompted."
debug_print "Regular move failed, trying with sudo"
if sudo mv "$FILE_NAME" "$dir/"; then
echo ""
echo "✅ Kosli CLI was successfully installed in $dir"
echo "Running 'kosli version' to verify:"
debug_print "Sudo installation successful, running version check"
kosli version
exit 0
fi
debug_print "Sudo move also failed for $dir"
fi
else
debug_print "Directory $dir either doesn't exist or is not in PATH"
fi
done

debug_print "All installation attempts failed"
echo ""
echo "Error: Could not install Kosli CLI."
echo "Please move the '$FILE_NAME' executable manually to a directory in your \$PATH."
echo "For example, you can run: sudo mv \"$FILE_NAME\" /usr/local/bin/"
exit 1