@@ -5,6 +5,75 @@ RED='\033[0;31m'
55MUTED=' \033[0;2m'
66NC=' \033[0m'
77
8+ # Sentry error telemetry — fire-and-forget error reporting via envelope API.
9+ # Uses the CLI's public write-only DSN. No PII collected.
10+ # Opt-out: SENTRY_CLI_NO_TELEMETRY=1
11+ SENTRY_DSN_KEY=" 1188a86f3f8168f089450587b00bca66"
12+ SENTRY_INGEST=" https://o1.ingest.us.sentry.io"
13+ SENTRY_PROJECT_ID=" 4510776311808000"
14+
15+ # Generate a UUID for the event. Tries /proc, uuidgen, then awk fallback.
16+ gen_uuid () {
17+ if [[ -r /proc/sys/kernel/random/uuid ]]; then
18+ cat /proc/sys/kernel/random/uuid
19+ elif command -v uuidgen > /dev/null 2>&1 ; then
20+ uuidgen | tr ' [:upper:]' ' [:lower:]'
21+ else
22+ awk ' BEGIN{srand();for(i=1;i<=32;i++)printf "%c",substr("0123456789abcdef",int(rand()*16)+1,1);print ""}'
23+ fi
24+ }
25+
26+ # Send an error event to Sentry. Runs in a subshell in the background so it
27+ # never blocks installation or propagates failures.
28+ # Usage: report_error "message" "step-name"
29+ report_error () {
30+ # Respect the same opt-out as the CLI binary
31+ [[ " ${SENTRY_CLI_NO_TELEMETRY:- } " == " 1" ]] && return 0
32+
33+ (
34+ set +e # Telemetry must never fail the script
35+ local msg=" ${1:- unknown error} "
36+ local step=" ${2:- unknown} "
37+ local event_id
38+ event_id=$( gen_uuid | tr -d ' -' )
39+ local timestamp
40+ timestamp=$( date -u +" %Y-%m-%dT%H:%M:%SZ" 2> /dev/null || echo " " )
41+
42+ # Escape a value for safe JSON string interpolation
43+ _esc () { printf ' %s' " $1 " | sed ' s/\\/\\\\/g;s/"/\\"/g' | tr ' \n' ' ' ; }
44+
45+ local json_msg; json_msg=$( _esc " $msg " )
46+ local json_step; json_step=$( _esc " $step " )
47+ local json_channel; json_channel=$( _esc " ${requested_version:- stable} " )
48+ local json_version; json_version=$( _esc " ${version:- unknown} " )
49+
50+ local envelope
51+ envelope=$( printf ' %s\n%s\n%s' \
52+ " {\" event_id\" :\" ${event_id} \" ,\" dsn\" :\" https://${SENTRY_DSN_KEY} @o1.ingest.us.sentry.io/${SENTRY_PROJECT_ID} \" }" \
53+ ' {"type":"event"}' \
54+ " {\" event_id\" :\" ${event_id} \" ,\" timestamp\" :\" ${timestamp} \" ,\" platform\" :\" other\" ,\" level\" :\" error\" ,\" logger\" :\" install\" ,\" server_name\" :\" install-script\" ,\" message\" :{\" formatted\" :\" ${json_msg} \" },\" tags\" :{\" os\" :\" ${os:- unknown} \" ,\" arch\" :\" ${arch:- unknown} \" ,\" channel\" :\" ${json_channel} \" ,\" step\" :\" ${json_step} \" ,\" install.version\" :\" ${json_version} \" },\" contexts\" :{\" runtime\" :{\" name\" :\" bash\" ,\" version\" :\" ${BASH_VERSION:- unknown} \" }}}" )
55+
56+ curl -sf --max-time 2 \
57+ -H " Content-Type: application/x-sentry-envelope" \
58+ -H " X-Sentry-Auth: Sentry sentry_key=${SENTRY_DSN_KEY} ,sentry_version=7" \
59+ -d " $envelope " \
60+ " ${SENTRY_INGEST} /api/${SENTRY_PROJECT_ID} /envelope/" \
61+ > /dev/null 2>&1
62+ ) &
63+ }
64+
65+ # Print error message, report to Sentry, and exit.
66+ # Usage: die "message" "step-name"
67+ die () {
68+ echo -e " ${RED} $1 ${NC} " >&2
69+ report_error " $1 " " ${2:- unknown} "
70+ wait 2> /dev/null || true # Let the background curl finish; ignore its exit status
71+ exit 1
72+ }
73+
74+ # Catch unexpected failures from set -e / pipefail (e.g., gunzip failing)
75+ trap ' die "Unexpected failure at line $LINENO" "trap"' ERR
76+
877usage () {
978 cat << EOF
1079Sentry CLI Installer
@@ -39,8 +108,7 @@ while [[ $# -gt 0 ]]; do
39108 requested_version=" $2 "
40109 shift 2
41110 else
42- echo -e " ${RED} Error: --version requires a version argument${NC} "
43- exit 1
111+ die " Error: --version requires a version argument" " args"
44112 fi
45113 ;;
46114 --no-modify-path)
@@ -60,24 +128,23 @@ case "$(uname -s)" in
60128 Darwin* ) os=" darwin" ;;
61129 Linux* ) os=" linux" ;;
62130 MINGW* |MSYS* |CYGWIN* ) os=" windows" ;;
63- * ) echo -e " ${RED} Unsupported OS: $( uname -s) ${NC} " ; exit 1 ;;
131+ * ) die " Unsupported OS: $( uname -s) " " detect-os " ;;
64132esac
65133
66134# Detect architecture
67135arch=$( uname -m)
68136case " $arch " in
69137 x86_64) arch=" x64" ;;
70138 aarch64|arm64) arch=" arm64" ;;
71- * ) echo -e " ${RED} Unsupported architecture: $arch ${NC} " ; exit 1 ;;
139+ * ) die " Unsupported architecture: $arch " " detect-arch " ;;
72140esac
73141
74142# Validate supported combinations
75143suffix=" "
76144if [[ " $os " == " windows" ]]; then
77145 suffix=" .exe"
78146 if [[ " $arch " != " x64" ]]; then
79- echo -e " ${RED} Unsupported: windows-$arch (only windows-x64 is supported)${NC} "
80- exit 1
147+ die " Unsupported: windows-$arch (only windows-x64 is supported)" " detect-arch"
81148 fi
82149fi
83150
@@ -103,8 +170,7 @@ if [[ "$requested_version" == "nightly" ]]; then
103170 " https://ghcr.io/token?scope=repository:getsentry/cli:pull" \
104171 | awk -F' "' ' {for(i=1;i<=NF;i++) if($i=="token"){print $(i+2);exit}}' )
105172 if [[ -z " $GHCR_TOKEN " ]]; then
106- echo -e " ${RED} Failed to get GHCR token${NC} "
107- exit 1
173+ die " Failed to get GHCR token" " ghcr-token"
108174 fi
109175
110176 # Step 2: Fetch the OCI manifest for the :nightly tag
@@ -113,16 +179,14 @@ if [[ "$requested_version" == "nightly" ]]; then
113179 -H " Accept: application/vnd.oci.image.manifest.v1+json" \
114180 " https://ghcr.io/v2/getsentry/cli/manifests/nightly" )
115181 if [[ -z " $MANIFEST " ]]; then
116- echo -e " ${RED} Failed to fetch nightly manifest from GHCR${NC} "
117- exit 1
182+ die " Failed to fetch nightly manifest from GHCR" " ghcr-manifest"
118183 fi
119184
120185 # Step 3: Extract version from manifest annotation
121186 version=$( echo " $MANIFEST " \
122187 | awk -F' "' ' {for(i=1;i<=NF;i++) if($i=="version"){print $(i+2);exit}}' )
123188 if [[ -z " $version " ]]; then
124- echo -e " ${RED} Failed to extract version from nightly manifest${NC} "
125- exit 1
189+ die " Failed to extract version from nightly manifest" " ghcr-version"
126190 fi
127191
128192 echo -e " ${MUTED} Installing nightly sentry ${version} ...${NC} "
@@ -140,8 +204,7 @@ if [[ "$requested_version" == "nightly" ]]; then
140204 }
141205 }' )
142206 if [[ -z " $digest " ]]; then
143- echo -e " ${RED} No nightly build found for ${gz_filename}${NC} "
144- exit 1
207+ die " No nightly build found for ${gz_filename} " " ghcr-digest"
145208 fi
146209
147210 # Step 5: Get the redirect URL from the blob endpoint (don't use -L: auth
@@ -150,8 +213,7 @@ if [[ "$requested_version" == "nightly" ]]; then
150213 -H " Authorization: Bearer $GHCR_TOKEN " \
151214 " https://ghcr.io/v2/getsentry/cli/blobs/${digest} " | tail -1)
152215 if [[ -z " $redir_url " ]]; then
153- echo -e " ${RED} Failed to get blob redirect URL from GHCR${NC} "
154- exit 1
216+ die " Failed to get blob redirect URL from GHCR" " ghcr-redirect"
155217 fi
156218
157219 # Step 6: Download the .gz blob and decompress (without auth header)
164226 version=$( curl -fsSL https://api.github.com/repos/getsentry/cli/releases/latest \
165227 | sed -n ' s/.*"tag_name": *"\([^"]*\)".*/\1/p' )
166228 if [[ -z " $version " ]]; then
167- echo -e " ${RED} Failed to fetch latest version${NC} "
168- exit 1
229+ die " Failed to fetch latest version" " gh-version"
169230 fi
170231 else
171232 version=" $requested_version "
0 commit comments