-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·190 lines (163 loc) · 6.49 KB
/
Copy pathinstall.sh
File metadata and controls
executable file
·190 lines (163 loc) · 6.49 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
#!/bin/sh
set -e
# stacked (st) installer script
# Usage: curl -fsSL https://raw.githubusercontent.com/andyrewlee/stacked/main/install.sh | sh
REPO="andyrewlee/stacked"
BINARY="st"
# Release archives are named by the goreleaser project ("stacked"), while the
# binary inside is "st".
ARCHIVE="stacked"
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
# Minisign public key used to verify the detached signature over checksums.txt
# before any checksum is trusted.
# PLACEHOLDER: the operator fills in the real base64 public key (the "RW…" line
# of the provisioned stacked.pub) when the release signing keypair is created —
# see the signing runbook in CONTRIBUTING.md's Releasing section. It must match
# the key file MINISIGN_KEY_FILE points to at release time (`make release`
# signs locally). While empty,
# signature verification cannot run and this script fails closed (see below).
MINISIGN_PUBKEY=""
# ST_ALLOW_UNVERIFIED=1 lets the install proceed on checksum-only
# verification when signature verification cannot run (minisign not installed,
# signature asset missing, or no public key embedded above). It never bypasses
# an actual failed signature check. Default: fail closed.
# Detect OS
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$OS" in
darwin) OS="darwin" ;;
linux) OS="linux" ;;
*)
echo "Error: Unsupported operating system: $OS"
exit 1
;;
esac
# Detect architecture
ARCH=$(uname -m)
case "$ARCH" in
x86_64|amd64) ARCH="amd64" ;;
arm64|aarch64) ARCH="arm64" ;;
*)
echo "Error: Unsupported architecture: $ARCH"
exit 1
;;
esac
# Called when signature verification cannot run at all; honors
# ST_ALLOW_UNVERIFIED (returns to continue on checksum-only) and otherwise
# fails closed.
skip_or_die() {
if [ "${ST_ALLOW_UNVERIFIED:-0}" = "1" ]; then
echo "Warning: $1; continuing with checksum-only verification (ST_ALLOW_UNVERIFIED=1)." >&2
return 0
fi
echo "Error: $1." >&2
echo "Refusing to install without signature verification." >&2
echo "Install minisign (https://jedisct1.github.io/minisign/) or set ST_ALLOW_UNVERIFIED=1 to proceed with checksum-only verification, at your own risk." >&2
exit 1
}
sha256_of() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$1" | awk '{print $1}'
elif command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$1" | awk '{print $1}'
else
echo ""
fi
}
# Get latest version from GitHub API
get_latest_version() {
curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" |
grep '"tag_name":' |
head -1 |
sed -E 's/.*"([^"]+)".*/\1/'
}
VERSION="${VERSION:-$(get_latest_version)}"
if [ -z "$VERSION" ]; then
echo "Error: Could not determine latest version"
exit 1
fi
# Remove 'v' prefix if present for filename
VERSION_NUM="${VERSION#v}"
FILENAME="${ARCHIVE}_${VERSION_NUM}_${OS}_${ARCH}.tar.gz"
# Release-asset base URL shared by the tarball, checksums, and signature
# fetches below. ST_INSTALL_BASE exists only as a test seam so scripts can run this
# script end-to-end against local snapshot artifacts over file://; it must
# never default anywhere other than the real GitHub release.
BASE="${ST_INSTALL_BASE:-https://github.com/${REPO}/releases/download/${VERSION}}"
DOWNLOAD_URL="${BASE}/${FILENAME}"
echo "Installing ${BINARY} ${VERSION} (${OS}/${ARCH})..."
# Create temp directory
TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"' EXIT
# Download and extract
echo "Downloading ${DOWNLOAD_URL}..."
curl -fsSL "$DOWNLOAD_URL" -o "${TMP_DIR}/${FILENAME}"
# Verify checksum against the release's published checksums.txt
CHECKSUMS_URL="${BASE}/checksums.txt"
echo "Fetching checksums..."
curl -fsSL "$CHECKSUMS_URL" -o "${TMP_DIR}/checksums.txt"
# Verify the minisign signature over checksums.txt before trusting any
# checksum in it. The checksum below stays as a second layer; the signature is
# what proves the checksums came from the release signing key rather than
# from whoever controls the release assets.
MINISIG_URL="${BASE}/checksums.txt.minisig"
echo "Fetching signature..."
if ! curl -fsSL "$MINISIG_URL" -o "${TMP_DIR}/checksums.txt.minisig"; then
skip_or_die "could not download release signature (checksums.txt.minisig)"
elif [ -z "$MINISIGN_PUBKEY" ]; then
skip_or_die "no release signing public key embedded in this installer"
elif ! command -v minisign >/dev/null 2>&1; then
skip_or_die "minisign not found; cannot verify the release signature"
elif ! minisign -V -P "$MINISIGN_PUBKEY" -m "${TMP_DIR}/checksums.txt" -x "${TMP_DIR}/checksums.txt.minisig" >/dev/null 2>&1; then
echo "Error: signature verification FAILED for checksums.txt — the release may have been tampered with." >&2
exit 1
else
echo "Signature verified."
fi
EXPECTED=$(grep " ${FILENAME}\$" "${TMP_DIR}/checksums.txt" | awk '{print $1}')
if [ -z "$EXPECTED" ]; then
echo "Error: no checksum entry for ${FILENAME} in checksums.txt"
exit 1
fi
ACTUAL=$(sha256_of "${TMP_DIR}/${FILENAME}")
if [ -z "$ACTUAL" ]; then
echo "Error: neither sha256sum nor shasum found; cannot verify download"
exit 1
fi
if [ "$EXPECTED" != "$ACTUAL" ]; then
echo "Error: checksum mismatch for ${FILENAME}"
echo " expected: $EXPECTED"
echo " actual: $ACTUAL"
exit 1
fi
echo "Checksum verified."
echo "Extracting..."
# Bounded extraction: refuse archives whose members would write outside
# TMP_DIR, then validate the binary member itself. The checksum/signature
# checks above are the outer gate, but this extraction also runs under the
# explicit ST_ALLOW_UNVERIFIED waiver, so the archive's shape is verified
# here regardless of who vouched for it.
if tar -tzf "${TMP_DIR}/${FILENAME}" | grep -qE '^/|(^|/)\.\.(/|$)'; then
echo "Error: ${FILENAME} contains a member outside the extract directory" >&2
exit 1
fi
tar -xzf "${TMP_DIR}/${FILENAME}" -C "$TMP_DIR"
# Only a regular file may be installed: a link member would land as a link
# at ${INSTALL_DIR}/${BINARY} (and chmod would follow it to the target),
# while archive-recorded mode bits (e.g. setuid) would survive mv unchecked.
if [ ! -f "${TMP_DIR}/${BINARY}" ] || [ -L "${TMP_DIR}/${BINARY}" ]; then
echo "Error: archive member ${BINARY} is missing or not a regular file" >&2
exit 1
fi
chmod 0755 "${TMP_DIR}/${BINARY}"
# Install binary
echo "Installing to ${INSTALL_DIR}/${BINARY}..."
if [ -w "$INSTALL_DIR" ]; then
mv "${TMP_DIR}/${BINARY}" "${INSTALL_DIR}/${BINARY}"
else
sudo mv "${TMP_DIR}/${BINARY}" "${INSTALL_DIR}/${BINARY}"
fi
chmod +x "${INSTALL_DIR}/${BINARY}"
echo ""
echo "✓ ${BINARY} ${VERSION} installed successfully!"
echo ""
echo "Run '${BINARY}' to get started."