@@ -13,7 +13,8 @@ Usage: install [options]
1313
1414Options:
1515 -h, --help Display this help message
16- -v, --version <version> Install a specific version (e.g., 0.2.0) or "nightly"
16+ -v, --version <version> Install a specific version (e.g., 0.2.0)
17+ --nightly Install the latest nightly build from GHCR
1718 --no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.)
1819 --no-completions Don't install shell completions
1920
@@ -24,11 +25,13 @@ Examples:
2425 curl -fsSL https://cli.sentry.dev/install | bash
2526 curl -fsSL https://cli.sentry.dev/install | bash -s -- --version nightly
2627 curl -fsSL https://cli.sentry.dev/install | bash -s -- --version 0.2.0
28+ curl -fsSL https://cli.sentry.dev/install | bash -s -- --nightly
2729 SENTRY_INSTALL_DIR=~/.local/bin curl -fsSL https://cli.sentry.dev/install | bash
2830EOF
2931}
3032
3133requested_version=" "
34+ nightly=false
3235no_modify_path=false
3336no_completions=false
3437while [[ $# -gt 0 ]]; do
@@ -43,6 +46,10 @@ while [[ $# -gt 0 ]]; do
4346 exit 1
4447 fi
4548 ;;
49+ --nightly)
50+ nightly=true
51+ shift
52+ ;;
4653 --no-modify-path)
4754 no_modify_path=true
4855 shift
@@ -81,61 +88,111 @@ if [[ "$os" == "windows" ]]; then
8188 fi
8289fi
8390
84- # Resolve version and download tag.
85- #
86- # "nightly" is a special value that installs from the rolling nightly prerelease
87- # built from the main branch. In this case both `version` and `download_tag`
88- # are set to the literal string "nightly".
89- #
90- # For stable releases both are the same version string (e.g. "0.5.0").
91- channel=" stable"
92- download_tag=" "
93-
94- if [[ -z " $requested_version " ]]; then
95- version=$( curl -fsSL https://api.github.com/repos/getsentry/cli/releases/latest | sed -n ' s/.*"tag_name": *"\([^"]*\)".*/\1/p' )
96- if [[ -z " $version " ]]; then
97- echo -e " ${RED} Failed to fetch latest version${NC} "
98- exit 1
99- fi
100- download_tag=" $version "
101- elif [[ " $requested_version " == " nightly" ]]; then
102- channel=" nightly"
103- download_tag=" nightly"
104- version=" nightly"
105- else
106- version=" $requested_version "
107- download_tag=" $requested_version "
91+ # Validate flag combinations
92+ if [[ " $nightly " == " true" && -n " $requested_version " ]]; then
93+ echo -e " ${RED} Error: --nightly and --version are mutually exclusive${NC} "
94+ exit 1
10895fi
10996
110- # Strip leading 'v' if present (releases use version without 'v' prefix)
111- version=" ${version# v} "
112- download_tag=" ${download_tag# v} "
113-
114- filename=" sentry-${os} -${arch}${suffix} "
115- url=" https://github.com/getsentry/cli/releases/download/${download_tag} /${filename} "
11697
11798# Download binary to a temp location
11899tmpdir=" ${TMPDIR:- ${TMP:- ${TEMP:-/ tmp} } } "
119100tmp_binary=" ${tmpdir} /sentry-install-$$ ${suffix} "
101+ version=" "
120102
121103# Clean up temp binary on failure (setup handles cleanup on success)
122104trap ' rm -f "$tmp_binary"' EXIT
123105
124- # For nightly the version string is literally "nightly", not a semver, so
125- # skip the "v" prefix that's only meaningful for numbered releases.
126- if [[ " $version " == " nightly" ]]; then
127- echo -e " ${MUTED} Downloading sentry nightly...${NC} "
106+ if [[ " $nightly " == " true" ]]; then
107+ # Nightly build: download from GHCR via OCI blob protocol.
108+ # No jq needed — parse JSON with awk.
109+ # ghcr.io blob downloads redirect to Azure Blob Storage. curl -L would
110+ # forward the Authorization header to Azure, which returns 404. Instead,
111+ # extract the redirect URL and follow it without the auth header.
112+
113+ echo -e " ${MUTED} Fetching nightly build from GHCR...${NC} "
114+
115+ # Step 1: Get anonymous pull token
116+ GHCR_TOKEN=$( curl -sf \
117+ " https://ghcr.io/token?scope=repository:getsentry/cli:pull" \
118+ | awk -F' "' ' {for(i=1;i<=NF;i++) if($i=="token"){print $(i+2);exit}}' )
119+ if [[ -z " $GHCR_TOKEN " ]]; then
120+ echo -e " ${RED} Failed to get GHCR token${NC} "
121+ exit 1
122+ fi
123+
124+ # Step 2: Fetch the OCI manifest for the :nightly tag
125+ MANIFEST=$( curl -sf \
126+ -H " Authorization: Bearer $GHCR_TOKEN " \
127+ -H " Accept: application/vnd.oci.image.manifest.v1+json" \
128+ " https://ghcr.io/v2/getsentry/cli/manifests/nightly" )
129+ if [[ -z " $MANIFEST " ]]; then
130+ echo -e " ${RED} Failed to fetch nightly manifest from GHCR${NC} "
131+ exit 1
132+ fi
133+
134+ # Step 3: Extract version from manifest annotation
135+ version=$( echo " $MANIFEST " \
136+ | awk -F' "' ' {for(i=1;i<=NF;i++) if($i=="version"){print $(i+2);exit}}' )
137+ if [[ -z " $version " ]]; then
138+ echo -e " ${RED} Failed to extract version from nightly manifest${NC} "
139+ exit 1
140+ fi
141+
142+ echo -e " ${MUTED} Installing nightly sentry ${version} ...${NC} "
143+
144+ # Step 4: Find the blob digest for this platform's .gz file
145+ gz_filename=" sentry-${os} -${arch}${suffix} .gz"
146+ digest=$( echo " $MANIFEST " \
147+ | sed ' s/},{/}\n{/g' \
148+ | awk -F' "' " /\" ${gz_filename// ./ \\ .} " ' /{for(i=1;i<=NF;i++) if($i=="digest"){print $(i+2);exit}}' )
149+ if [[ -z " $digest " ]]; then
150+ echo -e " ${RED} No nightly build found for ${gz_filename}${NC} "
151+ exit 1
152+ fi
153+
154+ # Step 5: Get the redirect URL from the blob endpoint (don't use -L: auth
155+ # header must NOT be forwarded to the Azure Blob Storage redirect target)
156+ redir_url=$( curl -s -w ' \n%{redirect_url}' -o /dev/null \
157+ -H " Authorization: Bearer $GHCR_TOKEN " \
158+ " https://ghcr.io/v2/getsentry/cli/blobs/${digest} " | tail -1)
159+ if [[ -z " $redir_url " ]]; then
160+ echo -e " ${RED} Failed to get blob redirect URL from GHCR${NC} "
161+ exit 1
162+ fi
163+
164+ # Step 6: Download the .gz blob and decompress (without auth header)
165+ curl -sf " $redir_url " | gunzip > " $tmp_binary "
166+
128167else
168+ # Stable build: resolve version and download from GitHub Releases.
169+
170+ if [[ -z " $requested_version " ]]; then
171+ version=$( curl -fsSL https://api.github.com/repos/getsentry/cli/releases/latest \
172+ | sed -n ' s/.*"tag_name": *"\([^"]*\)".*/\1/p' )
173+ if [[ -z " $version " ]]; then
174+ echo -e " ${RED} Failed to fetch latest version${NC} "
175+ exit 1
176+ fi
177+ else
178+ version=" $requested_version "
179+ fi
180+
181+ # Strip leading 'v' if present (releases use version without 'v' prefix)
182+ version=" ${version# v} "
183+ filename=" sentry-${os} -${arch}${suffix} "
184+ url=" https://github.com/getsentry/cli/releases/download/${version} /${filename} "
185+
129186 echo -e " ${MUTED} Downloading sentry v${version} ...${NC} "
130- fi
131187
132- # Try gzip-compressed download first (~60% smaller, ~37 MB vs ~99 MB).
133- # gunzip is POSIX and available on all Unix systems.
134- # Falls back to raw binary if the .gz asset doesn't exist yet.
135- if curl -fsSL " ${url} .gz" 2> /dev/null | gunzip > " $tmp_binary " 2> /dev/null; then
136- : # Compressed download succeeded
137- else
138- curl -fsSL --progress-bar " $url " -o " $tmp_binary "
188+ # Try gzip-compressed download first (~60% smaller, ~37 MB vs ~99 MB).
189+ # gunzip is POSIX and available on all Unix systems.
190+ # Falls back to raw binary if the .gz asset doesn't exist yet.
191+ if curl -fsSL " ${url} .gz" 2> /dev/null | gunzip > " $tmp_binary " 2> /dev/null; then
192+ : # Compressed download succeeded
193+ else
194+ curl -fsSL --progress-bar " $url " -o " $tmp_binary "
195+ fi
139196fi
140197
141198chmod +x " $tmp_binary "
@@ -145,6 +202,10 @@ chmod +x "$tmp_binary"
145202# completions, agent skills, and the welcome message.
146203# --channel persists the release channel so future `sentry cli upgrade`
147204# calls track the same channel without requiring a flag.
205+ channel=" nightly"
206+ if [[ " $nightly " != " true" ]]; then
207+ channel=" stable"
208+ fi
148209setup_args=" --install --method curl --channel $channel "
149210if [[ " $no_modify_path " == " true" ]]; then
150211 setup_args=" $setup_args --no-modify-path"
0 commit comments