mirror of
https://github.com/anomalyco/opencode.git
synced 2026-02-01 22:48:16 +00:00
302 lines
8.3 KiB
Bash
Executable File
302 lines
8.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
APP=opencode
|
||
|
||
MUTED='\033[0;2m'
|
||
RED='\033[0;31m'
|
||
ORANGE='\033[38;2;255;140;0m'
|
||
NC='\033[0m' # No Color
|
||
|
||
requested_version=${VERSION:-}
|
||
|
||
raw_os=$(uname -s)
|
||
os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
|
||
# Normalize various Unix-like identifiers
|
||
case "$raw_os" in
|
||
Darwin*) os="darwin" ;;
|
||
Linux*) os="linux" ;;
|
||
MINGW*|MSYS*|CYGWIN*) os="windows" ;;
|
||
esac
|
||
arch=$(uname -m)
|
||
|
||
if [[ "$arch" == "aarch64" ]]; then
|
||
arch="arm64"
|
||
elif [[ "$arch" == "x86_64" ]]; then
|
||
arch="x64"
|
||
fi
|
||
|
||
filename="$APP-$os-$arch.zip"
|
||
|
||
|
||
case "$filename" in
|
||
*"-linux-"*)
|
||
[[ "$arch" == "x64" || "$arch" == "arm64" ]] || exit 1
|
||
;;
|
||
*"-darwin-"*)
|
||
[[ "$arch" == "x64" || "$arch" == "arm64" ]] || exit 1
|
||
;;
|
||
*"-windows-"*)
|
||
[[ "$arch" == "x64" ]] || exit 1
|
||
;;
|
||
*)
|
||
echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}"
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
INSTALL_DIR=$HOME/.opencode/bin
|
||
mkdir -p "$INSTALL_DIR"
|
||
|
||
if [ -z "$requested_version" ]; then
|
||
url="https://github.com/sst/opencode/releases/latest/download/$filename"
|
||
specific_version=$(curl -s https://api.github.com/repos/sst/opencode/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
|
||
|
||
if [[ $? -ne 0 || -z "$specific_version" ]]; then
|
||
echo -e "${RED}Failed to fetch version information${NC}"
|
||
exit 1
|
||
fi
|
||
else
|
||
url="https://github.com/sst/opencode/releases/download/v${requested_version}/$filename"
|
||
specific_version=$requested_version
|
||
fi
|
||
|
||
print_message() {
|
||
local level=$1
|
||
local message=$2
|
||
local color=""
|
||
|
||
case $level in
|
||
info) color="${NC}" ;;
|
||
warning) color="${NC}" ;;
|
||
error) color="${RED}" ;;
|
||
esac
|
||
|
||
echo -e "${color}${message}${NC}"
|
||
}
|
||
|
||
check_version() {
|
||
if command -v opencode >/dev/null 2>&1; then
|
||
opencode_path=$(which opencode)
|
||
|
||
|
||
## TODO: check if version is installed
|
||
# installed_version=$(opencode version)
|
||
installed_version="0.0.1"
|
||
installed_version=$(echo $installed_version | awk '{print $2}')
|
||
|
||
if [[ "$installed_version" != "$specific_version" ]]; then
|
||
print_message info "${MUTED}Installed version: ${NC}$installed_version."
|
||
else
|
||
print_message info "${MUTED}Version ${NC}$specific_version${MUTED} already installed"
|
||
exit 0
|
||
fi
|
||
fi
|
||
}
|
||
|
||
unbuffered_sed() {
|
||
if echo | sed -u >/dev/null 2>&1; then
|
||
sed -nu "$@"
|
||
elif echo | sed -l >/dev/null 2>&1; then
|
||
sed -nl "$@"
|
||
else
|
||
local pad="$(printf "\n%512s" "")"
|
||
sed -ne "s/$/\\${pad}/" "$@"
|
||
fi
|
||
}
|
||
|
||
print_progress() {
|
||
local bytes="$1"
|
||
local length="$2"
|
||
[ "$length" -gt 0 ] || return 0
|
||
|
||
local width=50
|
||
local percent=$(( bytes * 100 / length ))
|
||
[ "$percent" -gt 100 ] && percent=100
|
||
local on=$(( percent * width / 100 ))
|
||
local off=$(( width - on ))
|
||
|
||
local full_filled=$(printf "%.0s■" {1..50})
|
||
local full_empty=$(printf "%.0s・" {1..50})
|
||
|
||
printf "\r${ORANGE}%s%s %3d%%${NC}" "${full_filled:0:$on}" "${full_empty:0:$off}" "$percent" >&4
|
||
}
|
||
|
||
download_with_progress() {
|
||
local url="$1"
|
||
local output="$2"
|
||
|
||
if [ -t 2 ]; then
|
||
exec 4>&2
|
||
else
|
||
exec 4>/dev/null
|
||
fi
|
||
|
||
local tmp_dir=${TMPDIR:-/tmp}
|
||
local basename="${tmp_dir}/opencode_install_$$"
|
||
local tracefile="${basename}.trace"
|
||
|
||
rm -f "$tracefile"
|
||
mkfifo "$tracefile"
|
||
|
||
# Hide cursor
|
||
printf "\033[?25l" >&4
|
||
|
||
trap "trap - RETURN; rm -f \"$tracefile\"; printf '\033[?25h' >&4; exec 4>&-" RETURN
|
||
|
||
(
|
||
curl --trace-ascii "$tracefile" -s -L -o "$output" "$url"
|
||
) &
|
||
local curl_pid=$!
|
||
|
||
unbuffered_sed \
|
||
-e 'y/ACDEGHLNORTV/acdeghlnortv/' \
|
||
-e '/^0000: content-length:/p' \
|
||
-e '/^<= recv data/p' \
|
||
"$tracefile" | \
|
||
{
|
||
local length=0
|
||
local bytes=0
|
||
|
||
while IFS=" " read -r -a line; do
|
||
local tag="${line[0]} ${line[1]}"
|
||
|
||
if [ "$tag" = "0000: content-length:" ]; then
|
||
length="${line[2]}"
|
||
length=$(echo "$length" | tr -d '\r')
|
||
bytes=0
|
||
elif [ "$tag" = "<= recv" ]; then
|
||
local size="${line[3]}"
|
||
bytes=$(( bytes + size ))
|
||
if [ "$length" -gt 0 ]; then
|
||
print_progress "$bytes" "$length"
|
||
fi
|
||
fi
|
||
done
|
||
}
|
||
|
||
wait $curl_pid
|
||
local ret=$?
|
||
echo "" >&4
|
||
return $ret
|
||
}
|
||
|
||
download_and_install() {
|
||
print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}version: ${NC}$specific_version"
|
||
mkdir -p opencodetmp && cd opencodetmp
|
||
|
||
if ! download_with_progress "$url" "$filename"; then
|
||
# Fallback to standard curl if custom fails for some reason
|
||
curl -# -L -o "$filename" "$url"
|
||
fi
|
||
|
||
unzip -q "$filename"
|
||
mv opencode "$INSTALL_DIR"
|
||
chmod 755 "${INSTALL_DIR}/opencode"
|
||
cd .. && rm -rf opencodetmp
|
||
}
|
||
|
||
check_version
|
||
download_and_install
|
||
|
||
|
||
add_to_path() {
|
||
local config_file=$1
|
||
local command=$2
|
||
|
||
if grep -Fxq "$command" "$config_file"; then
|
||
print_message info "Command already exists in $config_file, skipping write."
|
||
elif [[ -w $config_file ]]; then
|
||
echo -e "\n# opencode" >> "$config_file"
|
||
echo "$command" >> "$config_file"
|
||
print_message info "${MUTED}Successfully added ${NC}opencode ${MUTED}to \$PATH in ${NC}$config_file"
|
||
else
|
||
print_message warning "Manually add the directory to $config_file (or similar):"
|
||
print_message info " $command"
|
||
fi
|
||
}
|
||
|
||
XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config}
|
||
|
||
current_shell=$(basename "$SHELL")
|
||
case $current_shell in
|
||
fish)
|
||
config_files="$HOME/.config/fish/config.fish"
|
||
;;
|
||
zsh)
|
||
config_files="$HOME/.zshrc $HOME/.zshenv $XDG_CONFIG_HOME/zsh/.zshrc $XDG_CONFIG_HOME/zsh/.zshenv"
|
||
;;
|
||
bash)
|
||
config_files="$HOME/.bashrc $HOME/.bash_profile $HOME/.profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
|
||
;;
|
||
ash)
|
||
config_files="$HOME/.ashrc $HOME/.profile /etc/profile"
|
||
;;
|
||
sh)
|
||
config_files="$HOME/.ashrc $HOME/.profile /etc/profile"
|
||
;;
|
||
*)
|
||
# Default case if none of the above matches
|
||
config_files="$HOME/.bashrc $HOME/.bash_profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
|
||
;;
|
||
esac
|
||
|
||
config_file=""
|
||
for file in $config_files; do
|
||
if [[ -f $file ]]; then
|
||
config_file=$file
|
||
break
|
||
fi
|
||
done
|
||
|
||
if [[ -z $config_file ]]; then
|
||
print_message error "No config file found for $current_shell. Checked files: ${config_files[@]}"
|
||
exit 1
|
||
fi
|
||
|
||
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
|
||
case $current_shell in
|
||
fish)
|
||
add_to_path "$config_file" "fish_add_path $INSTALL_DIR"
|
||
;;
|
||
zsh)
|
||
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
|
||
;;
|
||
bash)
|
||
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
|
||
;;
|
||
ash)
|
||
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
|
||
;;
|
||
sh)
|
||
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
|
||
;;
|
||
*)
|
||
export PATH=$INSTALL_DIR:$PATH
|
||
print_message warning "Manually add the directory to $config_file (or similar):"
|
||
print_message info " export PATH=$INSTALL_DIR:\$PATH"
|
||
;;
|
||
esac
|
||
fi
|
||
|
||
if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then
|
||
echo "$INSTALL_DIR" >> $GITHUB_PATH
|
||
print_message info "Added $INSTALL_DIR to \$GITHUB_PATH"
|
||
fi
|
||
|
||
echo -e ""
|
||
echo -e "${MUTED} ${NC} ▄ "
|
||
echo -e "${MUTED}█▀▀█ █▀▀█ █▀▀█ █▀▀▄ ${NC}█▀▀▀ █▀▀█ █▀▀█ █▀▀█"
|
||
echo -e "${MUTED}█░░█ █░░█ █▀▀▀ █░░█ ${NC}█░░░ █░░█ █░░█ █▀▀▀"
|
||
echo -e "${MUTED}▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ${NC}▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀"
|
||
echo -e ""
|
||
echo -e ""
|
||
echo -e "${MUTED}To get started, navigate to a project and run:${NC}"
|
||
echo -e "opencode ${MUTED}Use free models${NC}"
|
||
echo -e "opencode auth login ${MUTED}Add paid provider API keys${NC}"
|
||
echo -e "opencode help ${MUTED}List commands and options${NC}"
|
||
echo -e ""
|
||
echo -e "${MUTED}For more information visit ${NC}https://opencode.ai/docs"
|
||
echo -e ""
|
||
echo -e ""
|
||
|