Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/install-scripts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Install Scripts

permissions:
contents: read

on:
push:
branches: [main]
paths:
- ".github/workflows/install-scripts.yml"
- "packages/cli/install.ps1"
pull_request:
paths:
- ".github/workflows/install-scripts.yml"
- "packages/cli/install.ps1"

jobs:
windows-powershell:
name: ${{ matrix.shell }} installs ${{ matrix.version || 'latest' }}
runs-on: windows-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
# pwsh is PowerShell 7; powershell is the Windows PowerShell 5.1 that
# ships with Windows. `version` empty means latest stable.
shell: [pwsh, powershell]
version: [""]
include:
# A legacy getsentry/cli release, resolved through the registry.
- shell: pwsh
version: "0.42.2"
defaults:
run:
shell: ${{ matrix.shell }}
env:
SENTRY_CLI_NO_TELEMETRY: "1"
SENTRY_VERSION: ${{ matrix.version }}
steps:
- uses: actions/checkout@v4

- name: Install through Invoke-Expression
run: |
$env:SENTRY_INSTALL_DIR = Join-Path $env:RUNNER_TEMP 'sentry-bin'
Get-Content -Raw -LiteralPath packages/cli/install.ps1 | Invoke-Expression
$installed = & (Join-Path $env:SENTRY_INSTALL_DIR 'sentry.exe') --version
if ($LASTEXITCODE -ne 0) { throw "sentry --version exited with $LASTEXITCODE" }
if ($env:SENTRY_VERSION -and "$installed".Trim() -ne $env:SENTRY_VERSION) {
throw "Installed $installed, expected $env:SENTRY_VERSION"
}
"Installed sentry $installed"

- name: Find sentry on PATH in a later step
run: |
$command = Get-Command sentry -ErrorAction Stop
"Resolved $($command.Source)"
sentry --version
if ($LASTEXITCODE -ne 0) { throw "sentry --version exited with $LASTEXITCODE" }
1 change: 1 addition & 0 deletions apps/cli-docs/public/install.ps1
22 changes: 21 additions & 1 deletion apps/cli-docs/src/content/docs/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ You can also set `SENTRY_INSTALL_DIR` to override the binary installation direct
curl https://cli.sentry.dev/install -fsS | SENTRY_INSTALL_DIR="$HOME/.local/bin" bash
```

### Windows (PowerShell)

On Windows, install the latest stable release from PowerShell:

```powershell
irm https://cli.sentry.dev/install.ps1 | iex
```

The PowerShell installer adds the install directory to your user `PATH`. It
reads the same `SENTRY_VERSION`, `SENTRY_INSTALL_DIR`, and `SENTRY_INIT`
environment variables as the install script. Set `SENTRY_NO_MODIFY_PATH=1` to
leave `PATH` unchanged:

```powershell
$env:SENTRY_VERSION = "0.42.2"; irm https://cli.sentry.dev/install.ps1 | iex
```

It installs stable releases only. To switch to nightly afterwards, run
`sentry cli upgrade nightly`.

### Supported Platforms

{/* GENERATED:START platform-support */}
Expand All @@ -81,7 +101,7 @@ curl https://cli.sentry.dev/install -fsS | SENTRY_INSTALL_DIR="$HOME/.local/bin"
<tr>
<td><strong>Windows</strong></td>
<td>x64</td>
<td>Via Git Bash, MSYS2, or WSL</td>
<td>Via PowerShell, Git Bash, MSYS2, or WSL</td>
</tr>
</tbody>
</table>
Expand Down
1 change: 1 addition & 0 deletions apps/cli-docs/src/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import sectionBg3 from '../../assets/section-bg-3.png';
<div class="hero-command">
<InstallSelector options={[
{ label: "curl", command: "curl https://cli.sentry.dev/install -fsS | bash" },
{ label: "powershell", command: "irm https://cli.sentry.dev/install.ps1 | iex" },
{ label: "brew", command: "brew install getsentry/tools/sentry" },
{ label: "npx", command: "npx sentry@latest" },
{ label: "npm", command: "npm install -g sentry" },
Expand Down
164 changes: 102 additions & 62 deletions packages/cli/install
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,11 @@ tmpdir="${TMPDIR:-${TMP:-${TEMP:-/tmp}}}"
tmp_binary="${tmpdir}/sentry-install-$$${suffix}"
blob_headers="${tmpdir}/sentry-install-headers-$$"
blob_file="${tmpdir}/sentry-install-blob-$$"
metadata_file="${tmpdir}/sentry-install-metadata-$$"
version=""

# Clean up temporary files after setup or on failure.
trap 'rm -f "$tmp_binary" "$blob_headers" "$blob_file"' EXIT
trap 'rm -f "$tmp_binary" "$blob_headers" "$blob_file" "$metadata_file"' EXIT

if [[ "$requested_version" == "nightly" ]]; then
# Nightly build: download from GHCR via OCI blob protocol.
Expand Down Expand Up @@ -273,19 +274,78 @@ if [[ "$requested_version" == "nightly" ]]; then
esac

else
# Stable build: resolve version and download from GitHub Releases.

if [[ -z "$requested_version" ]]; then
# Craft publishes this app's stable version to the release registry.
# Consume the full response: an early parser exit can cause SIGPIPE under pipefail.
if metadata=$(curl -fsSL --max-time 10 https://release-registry.services.sentry.io/apps/sentry/latest); then
version=$(printf '%s\n' "$metadata" \
| sed -n 's/.*"version": *"\([^"]*\)".*/\1/p')
# Stable build. Sentry's release registry is the source of truth for
# versions, download URLs (including legacy getsentry/cli releases), and
# checksums. GitHub is only a fallback for registry outages.
if [[ -n "$requested_version" ]]; then
version="${requested_version#cli@}"
version="${version#v}"
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
die "Invalid release version: ${version}" "resolve-version"
fi
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo -e "${MUTED}Could not resolve latest version from Sentry's release registry. Trying GitHub...${NC}" >&2
registry_path="$version"
else
registry_path="latest"
fi

asset="sentry-${os}-${arch}${suffix}"
gz_url=""
gz_sha=""
raw_url=""
raw_sha=""
registry_version=""
registry_status=$(curl -sS --max-time 10 -o "$metadata_file" -w '%{http_code}' \
"https://release-registry.services.sentry.io/apps/sentry/${registry_path}" 2>/dev/null) \
|| registry_status="000"
if [[ "$registry_status" == "200" ]]; then
# Asset names, URLs, and checksums contain no whitespace, so stripping it
# lets the parser below treat compact and pretty-printed JSON the same.
metadata=$(tr -d ' \t\r\n' < "$metadata_file")
registry_version=$(printf '%s\n' "$metadata" \
| sed -n 's/.*"version":"\([^"]*\)".*/\1/p')
if [[ "$registry_path" == "latest" && ! "$registry_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
registry_version=""
elif [[ "$registry_path" != "latest" && "$registry_version" != "$version" ]]; then
registry_version=""
fi
fi

if [[ -n "$registry_version" ]]; then
version="$registry_version"
# Print the url and sha256 of one `files` entry, one per line.
registry_file() {
printf '%s\n' "$metadata" | awk -v key="\"$1\":{" '
{
i = index($0, key)
if (!i) exit
s = substr($0, i + length(key) - 1)
n = length(s)
depth = 0
for (j = 1; j <= n; j++) {
c = substr(s, j, 1)
if (c == "{") depth++
else if (c == "}" && --depth == 0) break
}
obj = substr(s, 1, j)
url = ""
sha = ""
if (match(obj, /"url":"https:\/\/[^"]*"/)) url = substr(obj, RSTART + 7, RLENGTH - 8)
if (match(obj, /"sha256-hex":"[0-9a-f]+"/)) sha = substr(obj, RSTART + 14, RLENGTH - 15)
print url
print sha
}'
}
{ read -r gz_url || true; read -r gz_sha || true; } <<< "$(registry_file "${asset}.gz")"
{ read -r raw_url || true; read -r raw_sha || true; } <<< "$(registry_file "$asset")"
if [[ -z "$gz_url" && -z "$raw_url" ]]; then
die "Sentry CLI ${version} has no ${asset} build" "resolve-asset"
fi
elif [[ "$registry_status" == "404" && "$registry_path" != "latest" ]]; then
die "Unknown Sentry CLI version: ${version}" "resolve-version"
else
echo -e "${MUTED}Could not resolve ${registry_path} from Sentry's release registry. Trying GitHub...${NC}" >&2
if [[ "$registry_path" == "latest" ]]; then
release_tag=""
# GitHub may have many pages of MCP releases before its newest CLI release.
for page in {1..20}; do
if ! releases=$(curl -fsSL "https://api.github.com/repos/getsentry/toolkit/releases?per_page=100&page=${page}"); then
die "Failed to list Toolkit releases" "gh-version"
Expand Down Expand Up @@ -337,65 +397,45 @@ else
fi
done
if [[ -z "$release_tag" ]]; then
if [[ "$releases" != "[]" ]]; then
die "Toolkit release pagination exceeded 20 pages" "gh-version"
fi
# Before the first Toolkit CLI release, use the legacy version only
# after checking whether that exact Toolkit tag exists.
if ! legacy_release=$(curl -fsSL 'https://api.github.com/repos/getsentry/cli/releases/latest'); then
die "Failed to fetch latest stable version from Sentry's release registry and GitHub. Retry or install a specific version with --version <version>." "resolve-version"
fi
release_tag=$(printf '%s\n' "$legacy_release" | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p')
if [[ ! "${release_tag#v}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
die "Failed to fetch latest stable version from Sentry's release registry and GitHub. Retry or install a specific version with --version <version>." "resolve-version"
fi
die "Failed to fetch latest stable version from Sentry's release registry and GitHub. Retry or install a specific version with --version <version>." "resolve-version"
fi
version="${release_tag#cli@}"
version="${version#v}"
fi
else
version="$requested_version"
version="${version#cli@}"
version="${version#v}"
fi

if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
die "Invalid release version: ${version}" "gh-version"
gz_url="https://github.com/getsentry/toolkit/releases/download/cli@${version}/${asset}.gz"
raw_url="${gz_url%.gz}"
echo -e "${MUTED}Warning: GitHub fallback downloads are not checksum-verified.${NC}" >&2
fi

# Fall back to legacy only when GitHub confirms the exact Toolkit tag is absent.
if ! release_status=$(curl -sS -o /dev/null -w '%{http_code}' \
"https://api.github.com/repos/getsentry/toolkit/releases/tags/cli%40${version}"); then
die "Failed to check Toolkit release" "gh-version"
fi
case "$release_status" in
200) release_repo="getsentry/toolkit"; release_tag="cli@${version}" ;;
404)
if ! legacy_status=$(curl -sS -o /dev/null -w '%{http_code}' \
"https://api.github.com/repos/getsentry/cli/releases/tags/${version}"); then
die "Failed to check legacy CLI release" "gh-version"
fi
if [[ "$legacy_status" != "200" ]]; then
die "Legacy CLI release unavailable (HTTP ${legacy_status})" "gh-version"
fi
release_repo="getsentry/cli"
release_tag="$version"
;;
*) die "Toolkit release check failed (HTTP ${release_status})" "gh-version" ;;
esac

filename="sentry-${os}-${arch}${suffix}"
url="https://github.com/${release_repo}/releases/download/${release_tag}/${filename}"
# Usage: verify_sha256 <file> <expected-hex>; a missing checksum is a no-op.
verify_sha256() {
[[ -z "$2" ]] && return 0
local actual
if command -v sha256sum >/dev/null 2>&1; then
actual=$(sha256sum "$1" | awk '{print $1}')
elif command -v shasum >/dev/null 2>&1; then
actual=$(shasum -a 256 "$1" | awk '{print $1}')
else
echo -e "${MUTED}Warning: sha256sum and shasum are unavailable; skipping checksum verification.${NC}" >&2
return 0
fi
if [[ "$actual" != "$2" ]]; then
die "Checksum mismatch for ${asset} ${version}: expected $2, got ${actual}" "checksum"
fi
}

echo -e "${MUTED}Downloading sentry v${version}...${NC}"

# Try gzip-compressed download first (~60% smaller, ~37 MB vs ~99 MB).
# gunzip is POSIX and available on all Unix systems.
# Falls back to raw binary if the .gz asset doesn't exist yet.
if curl -fsSL "${url}.gz" 2>/dev/null | gunzip > "$tmp_binary" 2>/dev/null; then
: # Compressed download succeeded
# Prefer the gzip asset (~60% smaller, ~37 MB vs ~99 MB); fall back to the
# raw binary when it is missing.
if [[ -n "$gz_url" ]] && curl -fsSL -o "$blob_file" "$gz_url" 2>/dev/null; then
verify_sha256 "$blob_file" "$gz_sha"
gunzip -c "$blob_file" > "$tmp_binary"
elif [[ -n "$raw_url" ]]; then
curl -fsSL --progress-bar -o "$tmp_binary" "$raw_url" \
|| die "Failed to download ${raw_url}" "download"
verify_sha256 "$tmp_binary" "$raw_sha"
else
curl -fsSL --progress-bar "$url" -o "$tmp_binary"
die "Failed to download ${gz_url}" "download"
fi
fi

Expand Down
Loading
Loading