Skip to content

Commit 148fa77

Browse files
committed
Add --verify-checksum option to the installer
1 parent ce9a006 commit 148fa77

3 files changed

Lines changed: 188 additions & 11 deletions

File tree

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env bash
2+
3+
# Script to download releases and generate install.sh with SHA256 checksums
4+
# Usage: ./generate-install-script.sh <version>
5+
# Example: ./generate-install-script.sh v0.2.0
6+
7+
set -euo pipefail
8+
9+
VERSION="${1:-}"
10+
11+
if [ -z "$VERSION" ]; then
12+
echo "Usage: $0 <version>"
13+
echo "Example: $0 v0.2.0"
14+
exit 1
15+
fi
16+
17+
REPO="IvanIsCoding/celq"
18+
TEMP_DIR=$(mktemp -d)
19+
20+
cleanup() {
21+
rm -rf "$TEMP_DIR"
22+
}
23+
trap cleanup EXIT
24+
25+
echo "Downloading releases for $VERSION..."
26+
27+
# Download the archives (excluding FreeBSD)
28+
declare -A DOWNLOADS=(
29+
["macos-aarch64"]="celq-macos-aarch64.tar.gz"
30+
["macos-x86_64"]="celq-macos-x86_64.tar.gz"
31+
["windows-x86_64"]="celq-windows-x86_64.zip"
32+
["linux-x86_64-musl"]="celq-linux-x86_64-musl.tar.gz"
33+
["linux-aarch64-musl"]="celq-linux-aarch64-musl.tar.gz"
34+
["linux-x86_64-gnu"]="celq-linux-x86_64-gnu.tar.gz"
35+
["linux-aarch64-gnu"]="celq-linux-aarch64-gnu.tar.gz"
36+
)
37+
38+
declare -A CHECKSUMS
39+
40+
for key in "${!DOWNLOADS[@]}"; do
41+
filename="${DOWNLOADS[$key]}"
42+
echo "Downloading $filename..."
43+
gh release download "$VERSION" \
44+
--repo "$REPO" \
45+
--pattern "$filename" \
46+
--dir "$TEMP_DIR"
47+
48+
# Calculate SHA256
49+
if command -v sha256sum > /dev/null 2>&1; then
50+
checksum=$(sha256sum "$TEMP_DIR/$filename" | cut -d' ' -f1)
51+
elif command -v shasum > /dev/null 2>&1; then
52+
checksum=$(shasum -a 256 "$TEMP_DIR/$filename" | cut -d' ' -f1)
53+
else
54+
echo "Error: need sha256sum or shasum"
55+
exit 1
56+
fi
57+
58+
CHECKSUMS[$key]=$checksum
59+
echo " SHA256: $checksum"
60+
done
61+
62+
echo ""
63+
echo "Generating install.sh from template..."
64+
65+
# Read template and substitute values
66+
TEMPLATE_FILE="template_install.sh"
67+
OUTPUT_FILE="install.sh"
68+
69+
if [ ! -f "$TEMPLATE_FILE" ]; then
70+
echo "Error: $TEMPLATE_FILE not found"
71+
exit 1
72+
fi
73+
74+
# Create the output with all substitutions
75+
sed -e "s/{{CELQ_VERSION}}/${VERSION}/g" \
76+
-e "s/{{CHECKSUM_MACOS_AARCH64}}/${CHECKSUMS[macos-aarch64]}/g" \
77+
-e "s/{{CHECKSUM_MACOS_X86_64}}/${CHECKSUMS[macos-x86_64]}/g" \
78+
-e "s/{{CHECKSUM_WINDOWS_X86_64}}/${CHECKSUMS[windows-x86_64]}/g" \
79+
-e "s/{{CHECKSUM_LINUX_X86_64_MUSL}}/${CHECKSUMS[linux-x86_64-musl]}/g" \
80+
-e "s/{{CHECKSUM_LINUX_AARCH64_MUSL}}/${CHECKSUMS[linux-aarch64-musl]}/g" \
81+
-e "s/{{CHECKSUM_LINUX_X86_64_GNU}}/${CHECKSUMS[linux-x86_64-gnu]}/g" \
82+
-e "s/{{CHECKSUM_LINUX_AARCH64_GNU}}/${CHECKSUMS[linux-aarch64-gnu]}/g" \
83+
"$TEMPLATE_FILE" > "$OUTPUT_FILE"
84+
85+
chmod +x "$OUTPUT_FILE"
86+
87+
echo "✅ Generated $OUTPUT_FILE"
88+
echo ""
89+
echo "Checksums:"
90+
for key in "${!CHECKSUMS[@]}"; do
91+
echo " $key: ${CHECKSUMS[$key]}"
92+
done

.github/workflows/release_github.yml

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,14 @@ jobs:
183183
env:
184184
GH_TOKEN: ${{ github.token }}
185185
run: gh release upload ${VERSION} ${{ steps.archive.outputs.ARCHIVE_NAME }}
186-
186+
187187
update-install-script:
188188
name: Update install script on GitHub Pages
189189
runs-on: ubuntu-latest
190190
needs: [build, build-zig]
191191
environment: github_release
192192
steps:
193-
- name: Checkout celq repository (for template)
193+
- name: Checkout celq repository
194194
uses: actions/checkout@v4
195195
with:
196196
path: celq-repo
@@ -202,27 +202,28 @@ jobs:
202202
token: ${{ secrets.PAGES_DEPLOY_TOKEN }}
203203
path: pages-repo
204204

205-
- name: Generate install scripts
205+
- name: Generate install script with checksums
206+
env:
207+
GH_TOKEN: ${{ github.token }}
206208
run: |
209+
cd celq-repo
207210
VERSION="${{ github.ref_name }}"
208211
212+
# Run the generation script
213+
bash .github/scripts/generate-install-script.sh "$VERSION"
214+
209215
# Generate versioned install script (e.g., v0.1.1/install.sh or v0.2.0-beta.1/install.sh)
210-
mkdir -p "pages-repo/${VERSION}"
211-
sed "s/{{CELQ_VERSION}}/${VERSION}/g" celq-repo/template_install.sh > "pages-repo/${VERSION}/install.sh"
212-
chmod +x "pages-repo/${VERSION}/install.sh"
216+
mkdir -p "../pages-repo/${VERSION}"
217+
cp install.sh "../pages-repo/${VERSION}/install.sh"
213218
214219
# Only update root install.sh if this is NOT a pre-release
215220
# Pre-releases have a hyphen (e.g., v0.2.0-beta.1, v1.0.0-rc.1)
216221
if [[ ! "$VERSION" =~ -[a-zA-Z] ]]; then
217222
echo "Stable release detected, updating root install.sh"
218-
sed "s/{{CELQ_VERSION}}/${VERSION}/g" celq-repo/template_install.sh > pages-repo/install.sh
219-
chmod +x pages-repo/install.sh
223+
cp install.sh ../pages-repo/install.sh
220224
else
221225
echo "Pre-release detected, skipping root install.sh update"
222226
fi
223-
224-
# Copy generated script for GitHub release attachment
225-
cp "pages-repo/${VERSION}/install.sh" celq-repo/install.sh
226227
227228
- name: Generate attestation for install script
228229
uses: actions/attest-build-provenance@v3

template_install.sh

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ FLAGS:
3030
OPTIONS:
3131
--to LOCATION Where to install the binary [default: $CARGO_HOME/bin, $HOME/.cargo/bin, $HOME/.local/bin or $HOME/bin]
3232
--target TARGET Following Rust target triple conventions (e.g. x86_64-unknown-linux-gnu).
33+
--verify-checksum Verify the downloaded archive's SHA256 checksum
3334
--verify-attestation Verify the binary's GitHub Actions attestation (requires GitHub CLI with authentication)
3435
EOF
3536
}
@@ -87,6 +88,71 @@ check_attestation() {
8788
return 0
8889
}
8990

91+
# Get expected SHA256 checksum for a target
92+
get_expected_checksum() {
93+
local rust_target="$1"
94+
95+
case "$rust_target" in
96+
aarch64-apple-darwin)
97+
echo "{{CHECKSUM_MACOS_AARCH64}}"
98+
;;
99+
x86_64-apple-darwin)
100+
echo "{{CHECKSUM_MACOS_X86_64}}"
101+
;;
102+
x86_64-pc-windows-msvc)
103+
echo "{{CHECKSUM_WINDOWS_X86_64}}"
104+
;;
105+
x86_64-unknown-linux-musl)
106+
echo "{{CHECKSUM_LINUX_X86_64_MUSL}}"
107+
;;
108+
aarch64-unknown-linux-musl)
109+
echo "{{CHECKSUM_LINUX_AARCH64_MUSL}}"
110+
;;
111+
x86_64-unknown-linux-gnu)
112+
echo "{{CHECKSUM_LINUX_X86_64_GNU}}"
113+
;;
114+
aarch64-unknown-linux-gnu)
115+
echo "{{CHECKSUM_LINUX_AARCH64_GNU}}"
116+
;;
117+
*)
118+
err "No checksum available for target: $rust_target"
119+
;;
120+
esac
121+
}
122+
123+
verify_checksum() {
124+
local file="$1"
125+
local target="$2"
126+
127+
say "Verifying checksum for $file"
128+
129+
local expected_checksum
130+
expected_checksum=$(get_expected_checksum "$target")
131+
132+
if [ -z "$expected_checksum" ] || [ "$expected_checksum" = "{{CHECKSUM_"* ]; then
133+
err "Checksum template not populated for target: $target"
134+
fi
135+
136+
local actual_checksum
137+
if command -v sha256sum > /dev/null 2>&1; then
138+
actual_checksum=$(sha256sum "$file" | cut -d' ' -f1)
139+
elif command -v shasum > /dev/null 2>&1; then
140+
actual_checksum=$(shasum -a 256 "$file" | cut -d' ' -f1)
141+
else
142+
err "need sha256sum or shasum (command not found)"
143+
fi
144+
145+
if [ "$actual_checksum" != "$expected_checksum" ]; then
146+
say "error: checksum mismatch"
147+
say " expected: $expected_checksum"
148+
say " actual: $actual_checksum"
149+
return 1
150+
fi
151+
152+
say "Checksum verification successful"
153+
return 0
154+
}
155+
90156
# Map Rust target triple to pretty download name
91157
target_to_pretty_name() {
92158
local rust_target="$1"
@@ -107,6 +173,7 @@ target_to_pretty_name() {
107173

108174
force=false
109175
verify_attestation=false
176+
verify_checksums=false
110177
while test $# -gt 0; do
111178
case $1 in
112179
--force | -f)
@@ -127,6 +194,9 @@ while test $# -gt 0; do
127194
--verify-attestation)
128195
verify_attestation=true
129196
;;
197+
--verify-checksum)
198+
verify_checksums=true
199+
;;
130200
*)
131201
say "error: unrecognized argument '$1'. Usage:"
132202
help
@@ -151,6 +221,12 @@ if [ "$verify_attestation" = true ]; then
151221
need gh
152222
fi
153223

224+
if [ "$verify_checksums" = true ]; then
225+
command -v sha256sum > /dev/null 2>&1 ||
226+
command -v shasum > /dev/null 2>&1 ||
227+
err "need sha256sum or shasum (command not found)"
228+
fi
229+
154230
if [ -z "${dest-}" ]; then
155231
# Try to determine installation directory following Cargo conventions
156232
if [ -n "${CARGO_HOME-}" ]; then
@@ -232,6 +308,10 @@ if [ "$extension" = "zip" ]; then
232308
download "$archive" "$td/celq.zip"
233309
archive_file="$td/celq.zip"
234310

311+
if [ "$verify_checksums" = true ]; then
312+
verify_checksum "$archive_file" "$target" || err "Checksum verification failed"
313+
fi
314+
235315
if [ "$verify_attestation" = true ]; then
236316
check_attestation "$archive_file" || err "Attestation verification failed"
237317
fi
@@ -241,6 +321,10 @@ else
241321
download "$archive" "$td/celq.tar.gz"
242322
archive_file="$td/celq.tar.gz"
243323

324+
if [ "$verify_checksums" = true ]; then
325+
verify_checksum "$archive_file" "$target" || err "Checksum verification failed"
326+
fi
327+
244328
if [ "$verify_attestation" = true ]; then
245329
check_attestation "$archive_file" || err "Attestation verification failed"
246330
fi

0 commit comments

Comments
 (0)