Skip to content

Commit 212a8e6

Browse files
rawwerksclaude
andcommitted
Fix curl|bash race condition in install script
Wrap entire script in main() function to ensure bash reads the complete script before executing any of it. Previously, the inner curl call to fetch the release tag could interfere with the outer curl|bash pipe, causing "Failure writing output to destination" errors. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 173368e commit 212a8e6

1 file changed

Lines changed: 88 additions & 82 deletions

File tree

install.sh

Lines changed: 88 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,93 @@
11
#!/usr/bin/env bash
2+
# Wrapped in main() to prevent curl|bash race condition
23
set -euo pipefail
34

4-
REPO="rawwerks/dirpack"
5-
API_URL="https://api.github.com/repos/${REPO}/releases/latest"
6-
7-
fail() {
8-
echo "install.sh: $*" >&2
9-
exit 1
10-
}
11-
12-
require_cmd() {
13-
command -v "$1" >/dev/null 2>&1 || fail "missing required command: $1"
5+
main() {
6+
REPO="rawwerks/dirpack"
7+
API_URL="https://api.github.com/repos/${REPO}/releases/latest"
8+
9+
fail() {
10+
echo "install.sh: $*" >&2
11+
exit 1
12+
}
13+
14+
require_cmd() {
15+
command -v "$1" >/dev/null 2>&1 || fail "missing required command: $1"
16+
}
17+
18+
require_cmd curl
19+
require_cmd uname
20+
21+
tag="$(
22+
curl -fsSL "$API_URL" | grep -m1 '"tag_name"' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/'
23+
)"
24+
25+
if [ -z "$tag" ]; then
26+
fail "could not determine latest release tag"
27+
fi
28+
29+
os="$(uname -s)"
30+
case "$os" in
31+
Linux) os="linux" ;;
32+
Darwin) os="macos" ;;
33+
*) fail "unsupported OS: $os" ;;
34+
esac
35+
36+
arch="$(uname -m)"
37+
case "$arch" in
38+
x86_64|amd64) arch="x86_64" ;;
39+
arm64|aarch64) arch="aarch64" ;;
40+
*) fail "unsupported architecture: $arch" ;;
41+
esac
42+
43+
asset="dirpack-${os}-${arch}"
44+
url="https://github.com/${REPO}/releases/download/${tag}/${asset}"
45+
46+
tmpfile="$(mktemp)"
47+
cleanup() {
48+
rm -f "$tmpfile"
49+
}
50+
trap cleanup EXIT
51+
52+
echo "Downloading ${asset} from ${url}"
53+
curl -fL --progress-bar "$url" -o "$tmpfile" || fail "download failed"
54+
55+
chmod +x "$tmpfile"
56+
57+
install_dir="${HOME}/.local/bin"
58+
use_sudo=false
59+
60+
if [ ! -d "$install_dir" ]; then
61+
mkdir -p "$install_dir" 2>/dev/null || true
62+
fi
63+
64+
if [ ! -w "$install_dir" ]; then
65+
install_dir="/usr/local/bin"
66+
use_sudo=true
67+
fi
68+
69+
dest="${install_dir}/dirpack"
70+
71+
if [ "$use_sudo" = true ]; then
72+
require_cmd sudo
73+
sudo mv "$tmpfile" "$dest"
74+
else
75+
mv "$tmpfile" "$dest"
76+
fi
77+
78+
version="$("$dest" --version 2>/dev/null || true)"
79+
if [ -z "$version" ]; then
80+
fail "dirpack did not run successfully after install"
81+
fi
82+
83+
echo "Installed ${version} to ${dest}"
84+
85+
if ! command -v dirpack >/dev/null 2>&1; then
86+
echo "Note: ${install_dir} is not on your PATH."
87+
echo "Add this to your shell profile:"
88+
echo " export PATH=\"${install_dir}:\$PATH\""
89+
fi
1490
}
1591

16-
require_cmd curl
17-
require_cmd uname
18-
19-
tag="$(
20-
curl -fsSL "$API_URL" | grep -m1 '"tag_name"' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/'
21-
)"
22-
23-
if [ -z "$tag" ]; then
24-
fail "could not determine latest release tag"
25-
fi
26-
27-
os="$(uname -s)"
28-
case "$os" in
29-
Linux) os="linux" ;;
30-
Darwin) os="macos" ;;
31-
*) fail "unsupported OS: $os" ;;
32-
esac
33-
34-
arch="$(uname -m)"
35-
case "$arch" in
36-
x86_64|amd64) arch="x86_64" ;;
37-
arm64|aarch64) arch="aarch64" ;;
38-
*) fail "unsupported architecture: $arch" ;;
39-
esac
40-
41-
asset="dirpack-${os}-${arch}"
42-
url="https://github.com/${REPO}/releases/download/${tag}/${asset}"
43-
44-
tmpfile="$(mktemp)"
45-
cleanup() {
46-
rm -f "$tmpfile"
47-
}
48-
trap cleanup EXIT
49-
50-
echo "Downloading ${asset} from ${url}"
51-
curl -fL --progress-bar "$url" -o "$tmpfile" || fail "download failed"
52-
53-
chmod +x "$tmpfile"
54-
55-
install_dir="${HOME}/.local/bin"
56-
use_sudo=false
57-
58-
if [ ! -d "$install_dir" ]; then
59-
mkdir -p "$install_dir" 2>/dev/null || true
60-
fi
61-
62-
if [ ! -w "$install_dir" ]; then
63-
install_dir="/usr/local/bin"
64-
use_sudo=true
65-
fi
66-
67-
dest="${install_dir}/dirpack"
68-
69-
if [ "$use_sudo" = true ]; then
70-
require_cmd sudo
71-
sudo mv "$tmpfile" "$dest"
72-
else
73-
mv "$tmpfile" "$dest"
74-
fi
75-
76-
version="$("$dest" --version 2>/dev/null || true)"
77-
if [ -z "$version" ]; then
78-
fail "dirpack did not run successfully after install"
79-
fi
80-
81-
echo "Installed ${version} to ${dest}"
82-
83-
if ! command -v dirpack >/dev/null 2>&1; then
84-
echo "Note: ${install_dir} is not on your PATH."
85-
echo "Add this to your shell profile:"
86-
echo " export PATH=\"${install_dir}:\$PATH\""
87-
fi
92+
# Call main only after entire script is downloaded
93+
main "$@"

0 commit comments

Comments
 (0)