返回 CodeWhale
install.sh
根目录 / web / public / install.sh
1 #!/bin/sh
2 set -eu
3
4 repo="Hmbown/CodeWhale"
5 version="${CODEWHALE_VERSION:-latest}"
6 release_base="${CODEWHALE_RELEASE_BASE_URL:-${DEEPSEEK_TUI_RELEASE_BASE_URL:-}}"
7
8 usage() {
9 cat <<'USAGE'
10 Codewhale installer for macOS and Linux.
11
12 Usage:
13 curl -fsSL https://codewhale.net/install.sh | sh
14
15 Environment:
16 CODEWHALE_INSTALL_DIR Install directory. Default: $HOME/.local/bin
17 CODEWHALE_VERSION Release tag to install, for example v0.9.0. Default: latest
18 CODEWHALE_RELEASE_BASE_URL
19 Custom release asset base URL ending in /download
20 CODEWHALE_SKIP_GLIBC_CHECK=1
21 Skip Linux arm64 glibc compatibility preflight
22
23 Examples:
24 curl -fsSL https://codewhale.net/install.sh | CODEWHALE_INSTALL_DIR=/usr/local/bin sh
25 curl -fsSL https://codewhale.net/install.sh | CODEWHALE_VERSION=v0.9.0 sh
26 USAGE
27 }
28
29 case "${1:-}" in
30 -h|--help)
31 usage
32 exit 0
33 ;;
34 esac
35
36 say() {
37 printf '%s\n' "$*"
38 }
39
40 fail() {
41 printf 'codewhale install: %s\n' "$*" >&2
42 exit 1
43 }
44
45 if [ -n "${CODEWHALE_INSTALL_DIR:-}" ]; then
46 install_dir="$CODEWHALE_INSTALL_DIR"
47 else
48 [ -n "${HOME:-}" ] || fail "HOME is not set; set CODEWHALE_INSTALL_DIR"
49 install_dir="$HOME/.local/bin"
50 fi
51
52 need_cmd() {
53 command -v "$1" >/dev/null 2>&1 || fail "missing required command: $1"
54 }
55
56 download() {
57 url="$1"
58 out="$2"
59 if command -v curl >/dev/null 2>&1; then
60 curl -fsSL "$url" -o "$out"
61 elif command -v wget >/dev/null 2>&1; then
62 wget -q "$url" -O "$out"
63 else
64 fail "curl or wget is required"
65 fi
66 }
67
68 sha256_file() {
69 file="$1"
70 if command -v sha256sum >/dev/null 2>&1; then
71 sha256sum "$file" | awk '{print $1}'
72 elif command -v shasum >/dev/null 2>&1; then
73 shasum -a 256 "$file" | awk '{print $1}'
74 else
75 fail "sha256sum or shasum is required to verify downloads"
76 fi
77 }
78
79 verify_asset() {
80 asset="$1"
81 file="$2"
82 manifest="$3"
83 expected="$(
84 awk -v name="$asset" '
85 {
86 digest = tolower($1)
87 file = $2
88 sub(/^\*/, "", file)
89 if (file == name && digest ~ /^[0-9a-f]{64}$/) {
90 print digest
91 exit
92 }
93 }
94 ' "$manifest"
95 )"
96 [ -n "$expected" ] || fail "checksum not found for $asset"
97 actual="$(sha256_file "$file" | tr '[:upper:]' '[:lower:]')"
98 [ "$actual" = "$expected" ] || fail "checksum mismatch for $asset"
99 }
100
101 glibc_version() {
102 if command -v getconf >/dev/null 2>&1; then
103 getconf GNU_LIBC_VERSION 2>/dev/null | awk '{ print $NF; exit }'
104 return
105 fi
106 if command -v ldd >/dev/null 2>&1; then
107 ldd --version 2>/dev/null | awk 'NR == 1 {
108 for (i = 1; i <= NF; i++) {
109 if ($i ~ /^[0-9]+\.[0-9]+/) {
110 print $i
111 exit
112 }
113 }
114 }'
115 fi
116 }
117
118 version_at_least() {
119 have="$1"
120 need="$2"
121 awk -v have="$have" -v need="$need" '
122 BEGIN {
123 split(have, h, ".")
124 split(need, n, ".")
125 for (i = 1; i <= 3; i++) {
126 hv = h[i] + 0
127 nv = n[i] + 0
128 if (hv > nv) exit 0
129 if (hv < nv) exit 1
130 }
131 exit 0
132 }
133 '
134 }
135
136 check_glibc() {
137 case "$target" in
138 linux-arm64) ;;
139 *) return ;;
140 esac
141
142 [ "${CODEWHALE_SKIP_GLIBC_CHECK:-}" = "1" ] && return
143 [ "${DEEPSEEK_TUI_SKIP_GLIBC_CHECK:-}" = "1" ] && return
144 [ "${DEEPSEEK_SKIP_GLIBC_CHECK:-}" = "1" ] && return
145
146 required="2.39"
147 host="$(glibc_version || true)"
148 if [ -z "$host" ] || ! version_at_least "$host" "$required"; then
149 cat >&2 <<EOF
150 codewhale install: prebuilt Codewhale $target assets require glibc $required or newer.
151 This system reports glibc ${host:-unavailable}.
152
153 Linux x64 uses a static musl build. Linux arm64 release assets are GNU libc
154 builds from Ubuntu 24.04. Build from source with Cargo or set
155 CODEWHALE_SKIP_GLIBC_CHECK=1 to bypass this check at your own risk.
156 EOF
157 exit 1
158 fi
159 }
160
161 detect_platform() {
162 os="$(uname -s)"
163 arch="$(uname -m)"
164
165 case "$os" in
166 Darwin) platform="macos" ;;
167 Linux) platform="linux" ;;
168 *) fail "unsupported OS: $os. Use npm, Cargo, or the GitHub Releases page." ;;
169 esac
170
171 case "$arch" in
172 x86_64|amd64) cpu="x64" ;;
173 arm64|aarch64) cpu="arm64" ;;
174 riscv64) fail "Linux riscv64 prebuilt assets are temporarily unavailable because the locked rquickjs-sys dependency does not ship riscv64gc bindings." ;;
175 *) fail "unsupported CPU architecture: $arch. Use Cargo or build from source." ;;
176 esac
177
178 printf '%s-%s' "$platform" "$cpu"
179 }
180
181 if [ -z "$release_base" ]; then
182 if [ "$version" = "latest" ]; then
183 release_base="https://github.com/$repo/releases/latest/download"
184 else
185 release_base="https://github.com/$repo/releases/download/$version"
186 fi
187 fi
188
189 target="$(detect_platform)"
190 check_glibc
191 cli_asset="codewhale-$target"
192 tui_asset="codewhale-tui-$target"
193 manifest_asset="codewhale-artifacts-sha256.txt"
194
195 tmpdir="$(mktemp -d 2>/dev/null || mktemp -d -t codewhale-install)"
196 trap 'rm -rf "$tmpdir"' EXIT INT TERM
197
198 say "Installing Codewhale for $target"
199 say "Release assets: $release_base"
200 say "Install dir: $install_dir"
201
202 download "$release_base/$manifest_asset" "$tmpdir/$manifest_asset"
203 download "$release_base/$cli_asset" "$tmpdir/codewhale"
204 download "$release_base/$tui_asset" "$tmpdir/codewhale-tui"
205
206 verify_asset "$cli_asset" "$tmpdir/codewhale" "$tmpdir/$manifest_asset"
207 verify_asset "$tui_asset" "$tmpdir/codewhale-tui" "$tmpdir/$manifest_asset"
208 say "Checksums verified"
209
210 chmod 755 "$tmpdir/codewhale" "$tmpdir/codewhale-tui"
211 if command -v xattr >/dev/null 2>&1; then
212 xattr -d com.apple.quarantine "$tmpdir/codewhale" "$tmpdir/codewhale-tui" 2>/dev/null || true
213 fi
214
215 sudo_cmd=""
216 if [ -d "$install_dir" ]; then
217 if [ ! -w "$install_dir" ] ||
218 { [ -e "$install_dir/codewhale" ] && [ ! -w "$install_dir/codewhale" ]; } ||
219 { [ -e "$install_dir/codewhale-tui" ] && [ ! -w "$install_dir/codewhale-tui" ]; } ||
220 { [ -e "$install_dir/codew" ] && [ ! -w "$install_dir/codew" ]; }; then
221 need_cmd sudo
222 sudo_cmd="sudo"
223 fi
224 else
225 if ! mkdir -p "$install_dir" 2>/dev/null; then
226 need_cmd sudo
227 sudo mkdir -p "$install_dir"
228 sudo_cmd="sudo"
229 fi
230 fi
231
232 stage_cli="$install_dir/.codewhale.$$"
233 stage_tui="$install_dir/.codewhale-tui.$$"
234 trap 'rm -rf "$tmpdir"; rm -f "$stage_cli" "$stage_tui" 2>/dev/null || true' EXIT INT TERM
235
236 $sudo_cmd cp "$tmpdir/codewhale" "$stage_cli"
237 $sudo_cmd cp "$tmpdir/codewhale-tui" "$stage_tui"
238 $sudo_cmd chmod 755 "$stage_cli" "$stage_tui"
239 $sudo_cmd mv "$stage_cli" "$install_dir/codewhale"
240 $sudo_cmd mv "$stage_tui" "$install_dir/codewhale-tui"
241
242 $sudo_cmd rm -f "$install_dir/codew"
243 if ! $sudo_cmd ln -s codewhale "$install_dir/codew"; then
244 say "Installed binaries, but could not create $install_dir/codew alias"
245 fi
246
247 say "Installed:"
248 "$install_dir/codewhale" --version || true
249 "$install_dir/codewhale-tui" --version || true
250
251 case ":$PATH:" in
252 *":$install_dir:"*) ;;
253 *)
254 say ""
255 say "Add $install_dir to PATH to run codewhale from any terminal."
256 ;;
257 esac
258
259 say ""
260 say "Run: codewhale"
261
261 lines BASH