-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathinstall.sh
More file actions
379 lines (323 loc) · 10.5 KB
/
Copy pathinstall.sh
File metadata and controls
379 lines (323 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
#!/bin/sh
# Archgate installer for macOS, Linux, and Windows (Git Bash / MSYS2)
# Usage: curl -fsSL https://raw.githubusercontent.com/archgate/cli/main/install.sh | sh
set -eu
REPO="archgate/cli"
INSTALL_DIR="${ARCHGATE_INSTALL_DIR:-$HOME/.archgate/bin}"
# --- Detect platform ---
detect_platform() {
os="$(uname -s)"
arch="$(uname -m)"
case "$os" in
Darwin) platform="darwin" ;;
Linux) platform="linux" ;;
MINGW*|MSYS*) platform="win32" ;;
*)
echo "Error: unsupported OS: $os" >&2
echo "archgate supports macOS (ARM64), Linux (x86_64), and Windows (x64)." >&2
exit 1
;;
esac
case "$arch" in
arm64|aarch64) arch="arm64" ;;
x86_64|amd64) arch="x64" ;;
*)
echo "Error: unsupported architecture: $arch" >&2
echo "archgate supports arm64 (macOS) and x64 (Linux, Windows)." >&2
exit 1
;;
esac
# Validate supported combinations
if [ "$platform" = "darwin" ] && [ "$arch" != "arm64" ]; then
echo "Error: macOS is only supported on ARM64 (Apple Silicon)." >&2
exit 1
fi
if [ "$platform" = "linux" ] && [ "$arch" != "x64" ]; then
echo "Error: Linux is only supported on x86_64." >&2
exit 1
fi
if [ "$platform" = "win32" ] && [ "$arch" != "x64" ]; then
echo "Error: Windows is only supported on x86_64." >&2
exit 1
fi
ARTIFACT="archgate-${platform}-${arch}"
if [ "$platform" = "win32" ]; then
EXT="zip"
else
EXT="tar.gz"
fi
}
# --- Release asset helpers ---
asset_url() {
echo "https://github.com/${REPO}/releases/download/${1}/${ARTIFACT}.${EXT}"
}
# Returns 0 when the platform asset for the given version tag actually exists
# on GitHub Releases. A version being advertised (version.json, releases/latest)
# does not guarantee its assets are uploaded yet - releases are published
# before the binary build workflow finishes, and a failed release pipeline can
# advertise a version that never gets assets at all.
asset_exists() {
check_url="$(asset_url "$1")"
if command -v curl >/dev/null 2>&1; then
curl -fsIL -o /dev/null "$check_url" 2>/dev/null
elif command -v wget >/dev/null 2>&1; then
wget -q --spider "$check_url" 2>/dev/null
else
return 1
fi
}
# --- Resolve version ---
resolve_version() {
if [ -n "${ARCHGATE_VERSION:-}" ]; then
VERSION="$ARCHGATE_VERSION"
if ! asset_exists "$VERSION"; then
echo "Error: no ${ARTIFACT}.${EXT} asset found for pinned ARCHGATE_VERSION='${VERSION}'." >&2
echo "Check that the release exists and has finished building: https://github.com/${REPO}/releases" >&2
exit 1
fi
return
fi
# Primary: static version endpoint (no rate limits)
version_url="https://cli.archgate.dev/version.json"
static_response=""
if command -v curl >/dev/null 2>&1; then
static_response="$(curl -fsSL "$version_url" 2>/dev/null || true)"
elif command -v wget >/dev/null 2>&1; then
static_response="$(wget -qO- "$version_url" 2>/dev/null || true)"
fi
if [ -n "$static_response" ]; then
# tr -d '\r': jq on Windows (Git Bash) emits CRLF line endings, which
# would otherwise leave a stray carriage return in the parsed value.
if command -v jq >/dev/null 2>&1; then
static_version="$(printf '%s' "$static_response" | jq -r '.version // empty' | tr -d '\r')"
else
static_version="$(printf '%s' "$static_response" | grep '"version"' | sed 's/.*"version": *"//;s/".*//' | tr -d '\r')"
fi
if [ -n "$static_version" ]; then
# The version endpoint can advertise a release before its binaries are
# uploaded (or one whose release pipeline failed). Trust it only when
# the platform asset is actually downloadable.
if asset_exists "$static_version"; then
VERSION="$static_version"
return
fi
echo "Warning: ${static_version} is advertised but its release assets are not available yet (release may be in progress)." >&2
echo "Falling back to the newest installable release..." >&2
fi
fi
# Fallback: walk recent GitHub releases (newest first) and pick the first
# one whose platform asset exists. 'releases/latest' alone is not enough -
# it returns a release as soon as it is published, before assets upload.
api_url="https://api.github.com/repos/${REPO}/releases?per_page=10"
auth_header=""
if [ -n "${GITHUB_TOKEN:-}" ]; then
auth_header="Authorization: token ${GITHUB_TOKEN}"
fi
if command -v curl >/dev/null 2>&1; then
response="$(curl -fsSL ${auth_header:+-H "$auth_header"} "$api_url" || true)"
elif command -v wget >/dev/null 2>&1; then
response="$(wget -qO- ${auth_header:+--header="$auth_header"} "$api_url" 2>/dev/null || true)"
else
echo "Error: curl or wget is required." >&2
exit 1
fi
# Basic sanity check that we got a JSON array back
case "$response" in
\[*)
;;
*)
echo "Error: unexpected response from GitHub releases API." >&2
echo "Response (truncated): $(printf '%s' "$response" | cut -c1-200)" >&2
exit 1
;;
esac
# tr -d '\r': jq on Windows (Git Bash) emits CRLF line endings, which would
# otherwise leave a carriage return on every tag and fail validation below.
if command -v jq >/dev/null 2>&1; then
tags="$(printf '%s' "$response" | jq -r '.[].tag_name // empty' | tr -d '\r' || true)"
else
tags="$(printf '%s' "$response" | grep '"tag_name"' | sed 's/.*"tag_name": *"//;s/".*//' | tr -d '\r' || true)"
fi
if [ -z "$tags" ]; then
echo "Error: could not determine latest version (no release tags found)." >&2
exit 1
fi
for tag in $tags; do
# Validate that the tag looks reasonable before using it in a URL
case "$tag" in
*[!A-Za-z0-9._-]*) continue ;;
esac
if asset_exists "$tag"; then
VERSION="$tag"
return
fi
done
echo "Error: none of the recent releases have a ${ARTIFACT}.${EXT} asset." >&2
echo "Visit https://github.com/${REPO}/releases or https://cli.archgate.dev/getting-started/installation/ for alternative install methods." >&2
exit 1
}
# --- Download and install ---
download_and_install() {
url="$(asset_url "$VERSION")"
echo "Installing archgate ${VERSION} (${ARTIFACT})..."
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$url" -o "$tmpdir/archgate.${EXT}"
elif command -v wget >/dev/null 2>&1; then
wget -qO "$tmpdir/archgate.${EXT}" "$url"
else
echo "Error: neither 'curl' nor 'wget' is installed. Please install one of them to download archgate." >&2
exit 1
fi
mkdir -p "$INSTALL_DIR"
if [ "$platform" = "win32" ]; then
unzip -qo "$tmpdir/archgate.zip" -d "$tmpdir"
mv "$tmpdir/archgate.exe" "$INSTALL_DIR/archgate.exe"
else
tar -xzf "$tmpdir/archgate.tar.gz" -C "$tmpdir"
mv "$tmpdir/archgate" "$INSTALL_DIR/archgate"
chmod +x "$INSTALL_DIR/archgate"
fi
}
# --- Update PATH ---
get_shell_profiles() {
profiles=""
# Zsh
for f in "$HOME/.zshrc" "$HOME/.zshenv" "$HOME/.zprofile"; do
if [ -f "$f" ]; then
profiles="$profiles $f"
break
fi
done
# Bash — login vs interactive, Linux vs macOS
for f in "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile"; do
if [ -f "$f" ]; then
profiles="$profiles $f"
break
fi
done
# Fish
fish_config="$HOME/.config/fish/config.fish"
if [ -f "$fish_config" ]; then
profiles="$profiles $fish_config"
fi
# Nushell
for f in "$HOME/.config/nushell/env.nu" "$HOME/Library/Application Support/nushell/env.nu"; do
if [ -f "$f" ]; then
profiles="$profiles $f"
break
fi
done
# Ion
if [ -f "$HOME/.config/ion/initrc" ]; then
profiles="$profiles $HOME/.config/ion/initrc"
fi
# Csh / Tcsh
for f in "$HOME/.cshrc" "$HOME/.tcshrc"; do
if [ -f "$f" ]; then
profiles="$profiles $f"
break
fi
done
echo "$profiles"
}
path_line_for() {
file="$1"
case "$file" in
*.fish)
echo "fish_add_path ${INSTALL_DIR}" ;;
*env.nu)
echo "\$env.PATH = (\$env.PATH | prepend '${INSTALL_DIR}')" ;;
*initrc)
echo "let path = [ ${INSTALL_DIR} \$path ]" ;;
*.cshrc|*.tcshrc)
echo "set path = ( ${INSTALL_DIR} \$path )" ;;
*)
echo "export PATH=\"${INSTALL_DIR}:\$PATH\"" ;;
esac
}
already_configured() {
file="$1"
grep -qF "${INSTALL_DIR}" "$file" 2>/dev/null
}
setup_path() {
# Already on PATH — nothing to do
case ":${PATH}:" in
*":${INSTALL_DIR}:"*) return ;;
esac
profiles="$(get_shell_profiles)"
if [ -z "$profiles" ]; then
echo ""
echo "Could not detect any shell profile files."
echo "Manually add ${INSTALL_DIR} to your PATH."
return
fi
# Collect profiles that still need the PATH line
needs_update=""
already_done=""
for f in $profiles; do
if already_configured "$f"; then
already_done="$already_done $f"
else
needs_update="$needs_update $f"
fi
done
if [ -n "$already_done" ]; then
for f in $already_done; do
echo " Already configured: $f"
done
fi
if [ -z "$needs_update" ]; then
echo "PATH is already configured in all detected shell profiles."
return
fi
echo ""
echo "Detected shell profiles to update:"
for f in $needs_update; do
echo " $f -> $(path_line_for "$f")"
done
echo ""
# Prompt requires /dev/tty — available even when stdin is piped (curl | sh).
# Use a subshell to test the actual open, because -r may pass on CI runners
# that have /dev/tty present but no controlling terminal.
if ! (exec </dev/tty) 2>/dev/null; then
echo "No TTY available. To add archgate to your PATH manually, add the lines above to your shell profile."
return
fi
printf "Update these files now? [Y/n] "
if ! read -r answer </dev/tty 2>/dev/null; then
echo ""
echo "Could not read from terminal. To add archgate to your PATH manually, add the lines above to your shell profile."
return
fi
case "$answer" in
[nN]*)
echo ""
echo "Skipped. To add archgate to your PATH manually, add the lines above to your shell profile."
return
;;
esac
for f in $needs_update; do
line="$(path_line_for "$f")"
printf '\n# archgate\n%s\n' "$line" >> "$f"
echo " Updated: $f"
done
echo ""
echo "Restart your shell or open a new terminal to use archgate."
}
# --- Main ---
main() {
detect_platform
resolve_version
download_and_install
if [ "$platform" = "win32" ]; then
echo "archgate ${VERSION} installed to ${INSTALL_DIR}/archgate.exe"
else
echo "archgate ${VERSION} installed to ${INSTALL_DIR}/archgate"
fi
setup_path
echo ""
echo "Run 'archgate --help' to get started."
}
main