Skip to content

Commit 8e9a0c4

Browse files
Desktop: Install CLI (anomalyco#6526)
Co-authored-by: Brendan Allan <[email protected]>
1 parent ced093e commit 8e9a0c4

File tree

7 files changed

+364
-185
lines changed

7 files changed

+364
-185
lines changed

install

Lines changed: 132 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@ Usage: install.sh [options]
1616
Options:
1717
-h, --help Display this help message
1818
-v, --version <version> Install a specific version (e.g., 1.0.180)
19+
-b, --binary <path> Install from a local binary instead of downloading
1920
--no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.)
2021
2122
Examples:
2223
curl -fsSL https://opencode.ai/install | bash
2324
curl -fsSL https://opencode.ai/install | bash -s -- --version 1.0.180
25+
./install --binary /path/to/opencode
2426
EOF
2527
}
2628

2729
requested_version=${VERSION:-}
2830
no_modify_path=false
31+
binary_path=""
2932

3033
while [[ $# -gt 0 ]]; do
3134
case "$1" in
@@ -42,6 +45,15 @@ while [[ $# -gt 0 ]]; do
4245
exit 1
4346
fi
4447
;;
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+
;;
4557
--no-modify-path)
4658
no_modify_path=true
4759
shift
@@ -53,119 +65,128 @@ while [[ $# -gt 0 ]]; do
5365
esac
5466
done
5567

56-
raw_os=$(uname -s)
57-
os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
58-
case "$raw_os" in
59-
Darwin*) os="darwin" ;;
60-
Linux*) os="linux" ;;
61-
MINGW*|MSYS*|CYGWIN*) os="windows" ;;
62-
esac
63-
64-
arch=$(uname -m)
65-
if [[ "$arch" == "aarch64" ]]; then
66-
arch="arm64"
67-
fi
68-
if [[ "$arch" == "x86_64" ]]; then
69-
arch="x64"
70-
fi
68+
INSTALL_DIR=$HOME/.opencode/bin
69+
mkdir -p "$INSTALL_DIR"
7170

72-
if [ "$os" = "darwin" ] && [ "$arch" = "x64" ]; then
73-
rosetta_flag=$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)
74-
if [ "$rosetta_flag" = "1" ]; then
75-
arch="arm64"
76-
fi
77-
fi
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
7886

79-
combo="$os-$arch"
80-
case "$combo" in
81-
linux-x64|linux-arm64|darwin-x64|darwin-arm64|windows-x64)
82-
;;
83-
*)
84-
echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}"
85-
exit 1
86-
;;
87-
esac
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
8894

89-
archive_ext=".zip"
90-
if [ "$os" = "linux" ]; then
91-
archive_ext=".tar.gz"
92-
fi
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
93101

94-
is_musl=false
95-
if [ "$os" = "linux" ]; then
96-
if [ -f /etc/alpine-release ]; then
97-
is_musl=true
98-
fi
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
99111

100-
if command -v ldd >/dev/null 2>&1; then
101-
if ldd --version 2>&1 | grep -qi musl; then
102-
is_musl=true
112+
archive_ext=".zip"
113+
if [ "$os" = "linux" ]; then
114+
archive_ext=".tar.gz"
103115
fi
104-
fi
105-
fi
106116

107-
needs_baseline=false
108-
if [ "$arch" = "x64" ]; then
109-
if [ "$os" = "linux" ]; then
110-
if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then
111-
needs_baseline=true
112-
fi
113-
fi
117+
is_musl=false
118+
if [ "$os" = "linux" ]; then
119+
if [ -f /etc/alpine-release ]; then
120+
is_musl=true
121+
fi
114122

115-
if [ "$os" = "darwin" ]; then
116-
avx2=$(sysctl -n hw.optional.avx2_0 2>/dev/null || echo 0)
117-
if [ "$avx2" != "1" ]; then
118-
needs_baseline=true
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
119128
fi
120-
fi
121-
fi
122129

123-
target="$os-$arch"
124-
if [ "$needs_baseline" = "true" ]; then
125-
target="$target-baseline"
126-
fi
127-
if [ "$is_musl" = "true" ]; then
128-
target="$target-musl"
129-
fi
130-
131-
filename="$APP-$target$archive_ext"
130+
needs_baseline=false
131+
if [ "$arch" = "x64" ]; then
132+
if [ "$os" = "linux" ]; then
133+
if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then
134+
needs_baseline=true
135+
fi
136+
fi
132137

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+
fi
133145

134-
if [ "$os" = "linux" ]; then
135-
if ! command -v tar >/dev/null 2>&1; then
136-
echo -e "${RED}Error: 'tar' is required but not installed.${NC}"
137-
exit 1
146+
target="$os-$arch"
147+
if [ "$needs_baseline" = "true" ]; then
148+
target="$target-baseline"
138149
fi
139-
else
140-
if ! command -v unzip >/dev/null 2>&1; then
141-
echo -e "${RED}Error: 'unzip' is required but not installed.${NC}"
142-
exit 1
150+
if [ "$is_musl" = "true" ]; then
151+
target="$target-musl"
143152
fi
144-
fi
145153

146-
INSTALL_DIR=$HOME/.opencode/bin
147-
mkdir -p "$INSTALL_DIR"
154+
filename="$APP-$target$archive_ext"
148155

149-
if [ -z "$requested_version" ]; then
150-
url="https://github.com/anomalyco/opencode/releases/latest/download/$filename"
151-
specific_version=$(curl -s https://api.github.com/repos/anomalyco/opencode/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
152156

153-
if [[ $? -ne 0 || -z "$specific_version" ]]; then
154-
echo -e "${RED}Failed to fetch version information${NC}"
155-
exit 1
157+
if [ "$os" = "linux" ]; then
158+
if ! command -v tar >/dev/null 2>&1; then
159+
echo -e "${RED}Error: 'tar' is required but not installed.${NC}"
160+
exit 1
161+
fi
162+
else
163+
if ! command -v unzip >/dev/null 2>&1; then
164+
echo -e "${RED}Error: 'unzip' is required but not installed.${NC}"
165+
exit 1
166+
fi
156167
fi
157-
else
158-
# Strip leading 'v' if present
159-
requested_version="${requested_version#v}"
160-
url="https://github.com/anomalyco/opencode/releases/download/v${requested_version}/$filename"
161-
specific_version=$requested_version
162-
163-
# Verify the release exists before downloading
164-
http_status=$(curl -sI -o /dev/null -w "%{http_code}" "https://github.com/anomalyco/opencode/releases/tag/v${requested_version}")
165-
if [ "$http_status" = "404" ]; then
166-
echo -e "${RED}Error: Release v${requested_version} not found${NC}"
167-
echo -e "${MUTED}Available releases: https://github.com/anomalyco/opencode/releases${NC}"
168-
exit 1
168+
169+
if [ -z "$requested_version" ]; then
170+
url="https://github.com/anomalyco/opencode/releases/latest/download/$filename"
171+
specific_version=$(curl -s https://api.github.com/repos/anomalyco/opencode/releases/latest | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
172+
173+
if [[ $? -ne 0 || -z "$specific_version" ]]; then
174+
echo -e "${RED}Failed to fetch version information${NC}"
175+
exit 1
176+
fi
177+
else
178+
# Strip leading 'v' if present
179+
requested_version="${requested_version#v}"
180+
url="https://github.com/anomalyco/opencode/releases/download/v${requested_version}/$filename"
181+
specific_version=$requested_version
182+
183+
# Verify the release exists before downloading
184+
http_status=$(curl -sI -o /dev/null -w "%{http_code}" "https://github.com/anomalyco/opencode/releases/tag/v${requested_version}")
185+
if [ "$http_status" = "404" ]; then
186+
echo -e "${RED}Error: Release v${requested_version} not found${NC}"
187+
echo -e "${MUTED}Available releases: https://github.com/anomalyco/opencode/releases${NC}"
188+
exit 1
189+
fi
169190
fi
170191
fi
171192

@@ -267,11 +288,11 @@ download_with_progress() {
267288
{
268289
local length=0
269290
local bytes=0
270-
291+
271292
while IFS=" " read -r -a line; do
272293
[ "${#line[@]}" -lt 2 ] && continue
273294
local tag="${line[0]} ${line[1]}"
274-
295+
275296
if [ "$tag" = "0000: content-length:" ]; then
276297
length="${line[2]}"
277298
length=$(echo "$length" | tr -d '\r')
@@ -296,7 +317,7 @@ download_and_install() {
296317
print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}version: ${NC}$specific_version"
297318
local tmp_dir="${TMPDIR:-/tmp}/opencode_install_$$"
298319
mkdir -p "$tmp_dir"
299-
320+
300321
if [[ "$os" == "windows" ]] || ! [ -t 2 ] || ! download_with_progress "$url" "$tmp_dir/$filename"; then
301322
# Fallback to standard curl on Windows, non-TTY environments, or if custom progress fails
302323
curl -# -L -o "$tmp_dir/$filename" "$url"
@@ -307,14 +328,24 @@ download_and_install() {
307328
else
308329
unzip -q "$tmp_dir/$filename" -d "$tmp_dir"
309330
fi
310-
331+
311332
mv "$tmp_dir/opencode" "$INSTALL_DIR"
312333
chmod 755 "${INSTALL_DIR}/opencode"
313334
rm -rf "$tmp_dir"
314335
}
315336

316-
check_version
317-
download_and_install
337+
install_from_binary() {
338+
print_message info "\n${MUTED}Installing ${NC}opencode ${MUTED}from: ${NC}$binary_path"
339+
cp "$binary_path" "${INSTALL_DIR}/opencode"
340+
chmod 755 "${INSTALL_DIR}/opencode"
341+
}
342+
343+
if [ -n "$binary_path" ]; then
344+
install_from_binary
345+
else
346+
check_version
347+
download_and_install
348+
fi
318349

319350

320351
add_to_path() {
@@ -416,4 +447,3 @@ echo -e ""
416447
echo -e "${MUTED}For more information visit ${NC}https://opencode.ai/docs"
417448
echo -e ""
418449
echo -e ""
419-

packages/desktop/src-tauri/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/desktop/src-tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ serde_json = "1"
3535
tokio = "1.48.0"
3636
listeners = "0.3"
3737
tauri-plugin-os = "2"
38+
semver = "1.0.27"
3839

3940
[target.'cfg(target_os = "linux")'.dependencies]
4041
gtk = "0.18.2"

0 commit comments

Comments
 (0)