jerecom commited on
Commit
be7f980
Β·
verified Β·
1 Parent(s): 69854c7

Add local install script

Browse files
Files changed (1) hide show
  1. install.sh +460 -0
install.sh ADDED
@@ -0,0 +1,460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ APP=opencode
4
+
5
+ MUTED='\033[0;2m'
6
+ RED='\033[0;31m'
7
+ ORANGE='\033[38;5;214m'
8
+ NC='\033[0m' # No Color
9
+
10
+ usage() {
11
+ cat <<EOF
12
+ OpenCode Installer
13
+
14
+ Usage: install.sh [options]
15
+
16
+ Options:
17
+ -h, --help Display this help message
18
+ -v, --version <version> Install a specific version (e.g., 1.0.180)
19
+ -b, --binary <path> Install from a local binary instead of downloading
20
+ --no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.)
21
+
22
+ Examples:
23
+ curl -fsSL https://opencode.ai/install | bash
24
+ curl -fsSL https://opencode.ai/install | bash -s -- --version 1.0.180
25
+ ./install --binary /path/to/opencode
26
+ EOF
27
+ }
28
+
29
+ requested_version=${VERSION:-}
30
+ no_modify_path=false
31
+ binary_path=""
32
+
33
+ while [[ $# -gt 0 ]]; do
34
+ case "$1" in
35
+ -h|--help)
36
+ usage
37
+ exit 0
38
+ ;;
39
+ -v|--version)
40
+ if [[ -n "${2:-}" ]]; then
41
+ requested_version="$2"
42
+ shift 2
43
+ else
44
+ echo -e "${RED}Error: --version requires a version argument${NC}"
45
+ exit 1
46
+ fi
47
+ ;;
48
+ -b|--binary)
49
+ if [[ -n "${2:-}" ]]; then
50
+ binary_path="$2"
51
+ shift 2
52
+ else
53
+ echo -e "${RED}Error: --binary requires a path argument${NC}"
54
+ exit 1
55
+ fi
56
+ ;;
57
+ --no-modify-path)
58
+ no_modify_path=true
59
+ shift
60
+ ;;
61
+ *)
62
+ echo -e "${ORANGE}Warning: Unknown option '$1'${NC}" >&2
63
+ shift
64
+ ;;
65
+ esac
66
+ done
67
+
68
+ INSTALL_DIR=$HOME/.opencode/bin
69
+ mkdir -p "$INSTALL_DIR"
70
+
71
+ # If --binary is provided, skip all download/detection logic
72
+ if [ -n "$binary_path" ]; then
73
+ if [ ! -f "$binary_path" ]; then
74
+ echo -e "${RED}Error: Binary not found at ${binary_path}${NC}"
75
+ exit 1
76
+ fi
77
+ specific_version="local"
78
+ else
79
+ raw_os=$(uname -s)
80
+ os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
81
+ case "$raw_os" in
82
+ Darwin*) os="darwin" ;;
83
+ Linux*) os="linux" ;;
84
+ MINGW*|MSYS*|CYGWIN*) os="windows" ;;
85
+ esac
86
+
87
+ arch=$(uname -m)
88
+ if [[ "$arch" == "aarch64" ]]; then
89
+ arch="arm64"
90
+ fi
91
+ if [[ "$arch" == "x86_64" ]]; then
92
+ arch="x64"
93
+ fi
94
+
95
+ if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then
96
+ rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)
97
+ if [ "$rosetta_flag" = "1" ]; then
98
+ arch="arm64"
99
+ fi
100
+ fi
101
+
102
+ combo="$os-$arch"
103
+ case "$combo" in
104
+ linux-x64|linux-arm64|darwin-x64|darwin-arm64|windows-x64)
105
+ ;;
106
+ *)
107
+ echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}"
108
+ exit 1
109
+ ;;
110
+ esac
111
+
112
+ archive_ext=".zip"
113
+ if [ "$os" = "linux" ]; then
114
+ archive_ext=".tar.gz"
115
+ fi
116
+
117
+ is_musl=false
118
+ if [ "$os" = "linux" ]; then
119
+ if [ -f /etc/alpine-release ]; then
120
+ is_musl=true
121
+ fi
122
+
123
+ if command -v ldd >/dev/null 2>&1; then
124
+ if ldd --version 2>&1 | grep -qi musl; then
125
+ is_musl=true
126
+ fi
127
+ fi
128
+ fi
129
+
130
+ needs_baseline=false
131
+ if [ "$arch" = "x64" ]; then
132
+ if [ "$os" = "linux" ]; then
133
+ if ! grep -qwi avx2 /proc/cpuinfo 2>/dev/null; then
134
+ needs_baseline=true
135
+ fi
136
+ fi
137
+
138
+ if [ "$os" = "darwin" ]; then
139
+ avx2=$(sysctl -n hw.optional.avx2_0 2>/dev/null || echo 0)
140
+ if [ "$avx2" != "1" ]; then
141
+ needs_baseline=true
142
+ fi
143
+ fi
144
+
145
+ if [ "$os" = "windows" ]; then
146
+ ps="(Add-Type -MemberDefinition \"[DllImport(\"\"kernel32.dll\"\")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);\" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)"
147
+ out=""
148
+ if command -v powershell.exe >/dev/null 2>&1; then
149
+ out=$(powershell.exe -NoProfile -NonInteractive -Command "$ps" 2>/dev/null || true)
150
+ elif command -v pwsh >/dev/null 2>&1; then
151
+ out=$(pwsh -NoProfile -NonInteractive -Command "$ps" 2>/dev/null || true)
152
+ fi
153
+ out=$(echo "$out" | tr -d '\r' | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')
154
+ if [ "$out" != "true" ] && [ "$out" != "1" ]; then
155
+ needs_baseline=true
156
+ fi
157
+ fi
158
+ fi
159
+
160
+ target="$os-$arch"
161
+ if [ "$needs_baseline" = "true" ]; then
162
+ target="$target-baseline"
163
+ fi
164
+ if [ "$is_musl" = "true" ]; then
165
+ target="$target-musl"
166
+ fi
167
+
168
+ filename="$APP-$target$archive_ext"
169
+
170
+
171
+ if [ "$os" = "linux" ]; then
172
+ if ! command -v tar >/dev/null 2>&1; then
173
+ echo -e "${RED}Error: 'tar' is required but not installed.${NC}"
174
+ exit 1
175
+ fi
176
+ else
177
+ if ! command -v unzip >/dev/null 2>&1; then
178
+ echo -e "${RED}Error: 'unzip' is required but not installed.${NC}"
179
+ exit 1
180
+ fi
181
+ fi
182
+
183
+ if [ -z "$requested_version" ]; then
184
+ url="https://github.com/anomalyco/opencode/releases/latest/download/$filename"
185
+ specific_version=$(curl -s https://api.github.com/repos/anomalyco/opencode/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
186
+
187
+ if [[ $? -ne 0 || -z "$specific_version" ]]; then
188
+ echo -e "${RED}Failed to fetch version information${NC}"
189
+ exit 1
190
+ fi
191
+ else
192
+ # Strip leading 'v' if present
193
+ requested_version="${requested_version#v}"
194
+ url="https://github.com/anomalyco/opencode/releases/download/v${requested_version}/$filename"
195
+ specific_version=$requested_version
196
+
197
+ # Verify the release exists before downloading
198
+ http_status=$(curl -sI -o /dev/null -w "%{http_code}" "https://github.com/anomalyco/opencode/releases/tag/v${requested_version}")
199
+ if [ "$http_status" = "404" ]; then
200
+ echo -e "${RED}Error: Release v${requested_version} not found${NC}"
201
+ echo -e "${MUTED}Available releases: https://github.com/anomalyco/opencode/releases${NC}"
202
+ exit 1
203
+ fi
204
+ fi
205
+ fi
206
+
207
+ print_message() {
208
+ local level=$1
209
+ local message=$2
210
+ local color=""
211
+
212
+ case $level in
213
+ info) color="${NC}" ;;
214
+ warning) color="${NC}" ;;
215
+ error) color="${RED}" ;;
216
+ esac
217
+
218
+ echo -e "${color}${message}${NC}"
219
+ }
220
+
221
+ check_version() {
222
+ if command -v opencode >/dev/null 2>&1; then
223
+ opencode_path=$(which opencode)
224
+
225
+ ## Check the installed version
226
+ installed_version=$(opencode --version 2>/dev/null || echo "")
227
+
228
+ if [[ "$installed_version" != "$specific_version" ]]; then
229
+ print_message info "${MUTED}Installed version: ${NC}$installed_version."
230
+ else
231
+ print_message info "${MUTED}Version ${NC}$specific_version${MUTED} already installed"
232
+ exit 0
233
+ fi
234
+ fi
235
+ }
236
+
237
+ unbuffered_sed() {
238
+ if echo | sed -u -e "" >/dev/null 2>&1; then
239
+ sed -nu "$@"
240
+ elif echo | sed -l -e "" >/dev/null 2>&1; then
241
+ sed -nl "$@"
242
+ else
243
+ local pad="$(printf "\n%512s" "")"
244
+ sed -ne "s/$/\\${pad}/" "$@"
245
+ fi
246
+ }
247
+
248
+ print_progress() {
249
+ local bytes="$1"
250
+ local length="$2"
251
+ [ "$length" -gt 0 ] || return 0
252
+
253
+ local width=50
254
+ local percent=$(( bytes * 100 / length ))
255
+ [ "$percent" -gt 100 ] && percent=100
256
+ local on=$(( percent * width / 100 ))
257
+ local off=$(( width - on ))
258
+
259
+ local filled=$(printf "%*s" "$on" "")
260
+ filled=${filled// /β– }
261
+ local empty=$(printf "%*s" "$off" "")
262
+ empty=${empty// /ο½₯}
263
+
264
+ printf "\r${ORANGE}%s%s %3d%%${NC}" "$filled" "$empty" "$percent" >&4
265
+ }
266
+
267
+ download_with_progress() {
268
+ local url="$1"
269
+ local output="$2"
270
+
271
+ if [ -t 2 ]; then
272
+ exec 4>&2
273
+ else
274
+ exec 4>/dev/null
275
+ fi
276
+
277
+ local tmp_dir=${TMPDIR:-/tmp}
278
+ local basename="${tmp_dir}/opencode_install_$$"
279
+ local tracefile="${basename}.trace"
280
+
281
+ rm -f "$tracefile"
282
+ mkfifo "$tracefile"
283
+
284
+ # Hide cursor
285
+ printf "\033[?25l" >&4
286
+
287
+ trap "trap - RETURN; rm -f \"$tracefile\"; printf '\033[?25h' >&4; exec 4>&-" RETURN
288
+
289
+ (
290
+ curl --trace-ascii "$tracefile" -s -L -o "$output" "$url"
291
+ ) &
292
+ local curl_pid=$!
293
+
294
+ unbuffered_sed \
295
+ -e 'y/ACDEGHLNORTV/acdeghlnortv/' \
296
+ -e '/^0000: content-length:/p' \
297
+ -e '/^<= recv data/p' \
298
+ "$tracefile" | \
299
+ {
300
+ local length=0
301
+ local bytes=0
302
+
303
+ while IFS=" " read -r -a line; do
304
+ [ "${#line[@]}" -lt 2 ] && continue
305
+ local tag="${line[0]} ${line[1]}"
306
+
307
+ if [ "$tag" = "0000: content-length:" ]; then
308
+ length="${line[2]}"
309
+ length=$(echo "$length" | tr -d '\r')
310
+ bytes=0
311
+ elif [ "$tag" = "<= recv" ]; then
312
+ local size="${line[3]}"
313
+ bytes=$(( bytes + size ))
314
+ if [ "$length" -gt 0 ]; then
315
+ print_progress "$bytes" "$length"
316
+ fi
317
+ fi
318
+ done
319
+ }
320
+
321
+ wait $curl_pid
322
+ local ret=$?
323
+ echo "" >&4
324
+ return $ret
325
+ }
326
+
327
+ download_and_install() {
328
+ print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}version: ${NC}$specific_version"
329
+ local tmp_dir="${TMPDIR:-/tmp}/opencode_install_$$"
330
+ mkdir -p "$tmp_dir"
331
+
332
+ if [[ "$os" == "windows" ]] || ! [ -t 2 ] || ! download_with_progress "$url" "$tmp_dir/$filename"; then
333
+ # Fallback to standard curl on Windows, non-TTY environments, or if custom progress fails
334
+ curl -# -L -o "$tmp_dir/$filename" "$url"
335
+ fi
336
+
337
+ if [ "$os" = "linux" ]; then
338
+ tar -xzf "$tmp_dir/$filename" -C "$tmp_dir"
339
+ else
340
+ unzip -q "$tmp_dir/$filename" -d "$tmp_dir"
341
+ fi
342
+
343
+ mv "$tmp_dir/opencode" "$INSTALL_DIR"
344
+ chmod 755 "${INSTALL_DIR}/opencode"
345
+ rm -rf "$tmp_dir"
346
+ }
347
+
348
+ install_from_binary() {
349
+ print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}from: ${NC}$binary_path"
350
+ cp "$binary_path" "${INSTALL_DIR}/opencode"
351
+ chmod 755 "${INSTALL_DIR}/opencode"
352
+ }
353
+
354
+ if [ -n "$binary_path" ]; then
355
+ install_from_binary
356
+ else
357
+ check_version
358
+ download_and_install
359
+ fi
360
+
361
+
362
+ add_to_path() {
363
+ local config_file=$1
364
+ local command=$2
365
+
366
+ if grep -Fxq "$command" "$config_file"; then
367
+ print_message info "Command already exists in $config_file, skipping write."
368
+ elif [[ -w $config_file ]]; then
369
+ echo -e "\n# opencode" >> "$config_file"
370
+ echo "$command" >> "$config_file"
371
+ print_message info "${MUTED}Successfully added ${NC}opencode ${MUTED}to \$PATH in ${NC}$config_file"
372
+ else
373
+ print_message warning "Manually add the directory to $config_file (or similar):"
374
+ print_message info " $command"
375
+ fi
376
+ }
377
+
378
+ XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config}
379
+
380
+ current_shell=$(basename "$SHELL")
381
+ case $current_shell in
382
+ fish)
383
+ config_files="$HOME/.config/fish/config.fish"
384
+ ;;
385
+ zsh)
386
+ config_files="${ZDOTDIR:-$HOME}/.zshrc ${ZDOTDIR:-$HOME}/.zshenv $XDG_CONFIG_HOME/zsh/.zshrc $XDG_CONFIG_HOME/zsh/.zshenv"
387
+ ;;
388
+ bash)
389
+ config_files="$HOME/.bashrc $HOME/.bash_profile $HOME/.profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
390
+ ;;
391
+ ash)
392
+ config_files="$HOME/.ashrc $HOME/.profile /etc/profile"
393
+ ;;
394
+ sh)
395
+ config_files="$HOME/.ashrc $HOME/.profile /etc/profile"
396
+ ;;
397
+ *)
398
+ # Default case if none of the above matches
399
+ config_files="$HOME/.bashrc $HOME/.bash_profile $XDG_CONFIG_HOME/bash/.bashrc $XDG_CONFIG_HOME/bash/.bash_profile"
400
+ ;;
401
+ esac
402
+
403
+ if [[ "$no_modify_path" != "true" ]]; then
404
+ config_file=""
405
+ for file in $config_files; do
406
+ if [[ -f $file ]]; then
407
+ config_file=$file
408
+ break
409
+ fi
410
+ done
411
+
412
+ if [[ -z $config_file ]]; then
413
+ print_message warning "No config file found for $current_shell. You may need to manually add to PATH:"
414
+ print_message info " export PATH=$INSTALL_DIR:\$PATH"
415
+ elif [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
416
+ case $current_shell in
417
+ fish)
418
+ add_to_path "$config_file" "fish_add_path $INSTALL_DIR"
419
+ ;;
420
+ zsh)
421
+ add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
422
+ ;;
423
+ bash)
424
+ add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
425
+ ;;
426
+ ash)
427
+ add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
428
+ ;;
429
+ sh)
430
+ add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
431
+ ;;
432
+ *)
433
+ export PATH=$INSTALL_DIR:$PATH
434
+ print_message warning "Manually add the directory to $config_file (or similar):"
435
+ print_message info " export PATH=$INSTALL_DIR:\$PATH"
436
+ ;;
437
+ esac
438
+ fi
439
+ fi
440
+
441
+ if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then
442
+ echo "$INSTALL_DIR" >> $GITHUB_PATH
443
+ print_message info "Added $INSTALL_DIR to \$GITHUB_PATH"
444
+ fi
445
+
446
+ echo -e ""
447
+ echo -e "${MUTED}Β  ${NC} β–„ "
448
+ echo -e "${MUTED}β–ˆβ–€β–€β–ˆ β–ˆβ–€β–€β–ˆ β–ˆβ–€β–€β–ˆ β–ˆβ–€β–€β–„ ${NC}β–ˆβ–€β–€β–€ β–ˆβ–€β–€β–ˆ β–ˆβ–€β–€β–ˆ β–ˆβ–€β–€β–ˆ"
449
+ echo -e "${MUTED}β–ˆβ–‘β–‘β–ˆ β–ˆβ–‘β–‘β–ˆ β–ˆβ–€β–€β–€ β–ˆβ–‘β–‘β–ˆ ${NC}β–ˆβ–‘β–‘β–‘ β–ˆβ–‘β–‘β–ˆ β–ˆβ–‘β–‘β–ˆ β–ˆβ–€β–€β–€"
450
+ echo -e "${MUTED}β–€β–€β–€β–€ β–ˆβ–€β–€β–€ β–€β–€β–€β–€ β–€ β–€ ${NC}β–€β–€β–€β–€ β–€β–€β–€β–€ β–€β–€β–€β–€ β–€β–€β–€β–€"
451
+ echo -e ""
452
+ echo -e ""
453
+ echo -e "${MUTED}OpenCode includes free models, to start:${NC}"
454
+ echo -e ""
455
+ echo -e "cd <project> ${MUTED}# Open directory${NC}"
456
+ echo -e "opencode ${MUTED}# Run command${NC}"
457
+ echo -e ""
458
+ echo -e "${MUTED}For more information visit ${NC}https://opencode.ai/docs"
459
+ echo -e ""
460
+ echo -e ""