Skip to content

Commit 0898abe

Browse files
feat(release): add release command group and CI finalization (#628)
## Summary Adds the `sentry release` command group — the #1 critical gap identified in #600 — with 10 subcommands for full release lifecycle management, plus a GitHub Actions workflow for automated release finalization. ## Commands | Command | Description | |---------|-------------| | `release list` | Paginated org-scoped release listing | | `release view` | Release detail view | | `release create` | Create with `--project`, `--finalize`, `--ref`, `--url`, `--dry-run` | | `release finalize` | Set `dateReleased` (custom timestamp via `--released`), `--dry-run` | | `release delete` | `buildDeleteCommand` — auto `--yes`/`--force`/`--dry-run`, type-to-confirm | | `release deploy` | Positional `<version> <environment> [name]`, `--time`, `--dry-run` | | `release deploys` | List deploys for a release | | `release set-commits` | `--auto`, `--local`, `--commit REPO@SHA` (ranges: `REPO@PREV..SHA`), `--clear` | | `release propose-version` | Detect from 15 CI env vars (GitHub, Vercel, Heroku, etc.) or git HEAD | | `releases` (alias) | → `release list` | ## Key design decisions - **`buildDeleteCommand`** for `release delete` — auto-injects `--yes`/`--force`/`--dry-run` flags, non-interactive guard, uses `confirmByTyping` from shared infrastructure - **`DRY_RUN_FLAG`** on `create`, `finalize`, `deploy` — consistent `--dry-run`/`-n` support across all mutation commands - **`--commit` supports ranges** — `REPO@PREV..SHA` sends refs format to the API, matching the reference sentry-cli - **`set-commits` default mode** — tries auto-discovery, falls back to local git with warning. Per-org negative cache in the `metadata` table (1-hour TTL) skips the speculative API call on subsequent runs - **No `simple-git` dependency** — `execFileSync` (no shell) from `node:child_process` for all git operations. Avoids shell injection and works natively in both Bun and Node distributions - **`@sentry/api` types** — uses `OrgReleaseResponse` and `DeployResponse` directly from the SDK instead of redundant Zod schemas - **Centralized git helpers** — `src/lib/git.ts` consolidates primitives used by both release commands and `sentry init` wizard. `src/lib/init/git.ts` is now a thin adapter - **`propose-version`** checks 15 CI env vars (matching the reference impl) before falling back to git HEAD ## CI integration `.github/workflows/sentry-release.yml` — triggered on `release: published`, installs the just-published npm package via `npm install -g sentry@$VERSION`, then runs create → set-commits → finalize → deploy. ## Testing - Property-based tests for `parseReleaseArg` and `parseRemoteUrl` (fast-check) - Unit tests for view, create, finalize, deploy, propose-version, set-commits commands - API tests with `globalThis.fetch` mocking for all 11 exported API functions - 83 tests, 11,778 assertions ## New files (35 changed, +3913 / -103) - `src/lib/git.ts` — centralized git helpers (`execFileSync`, no shell) - `src/lib/api/releases.ts` — 11 API functions - `src/commands/release/` — 10 command files + arg parser - `.github/workflows/sentry-release.yml` — CI finalization workflow - `test/` — 10 test files Ref: #600 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 99be95c commit 0898abe

36 files changed

Lines changed: 4026 additions & 103 deletions
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Sentry Release
2+
on:
3+
release:
4+
types: [published]
5+
6+
permissions:
7+
contents: read
8+
9+
jobs:
10+
finalize:
11+
name: Finalize Sentry Release
12+
runs-on: ubuntu-latest
13+
# Skip pre-releases (nightlies, dev versions)
14+
if: "!github.event.release.prerelease"
15+
env:
16+
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
17+
# Tag names are bare semver (e.g., "0.24.0", no "v" prefix),
18+
# matching both the npm package version and Sentry release version.
19+
VERSION: ${{ github.event.release.tag_name }}
20+
steps:
21+
- name: Install CLI
22+
run: npm install -g "sentry@${VERSION}"
23+
24+
- name: Create release
25+
run: sentry release create "sentry/${VERSION}" --project cli
26+
27+
- name: Set commits
28+
continue-on-error: true
29+
run: sentry release set-commits "sentry/${VERSION}" --auto
30+
31+
- name: Finalize release
32+
run: sentry release finalize "sentry/${VERSION}"
33+
34+
- name: Create deploy
35+
run: sentry release deploy "sentry/${VERSION}" production

AGENTS.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ bun run test:unit # Run unit tests only
6161
bun run test:e2e # Run e2e tests only
6262
```
6363

64+
## Rules: No Runtime Dependencies
65+
66+
**CRITICAL**: All packages must be in `devDependencies`, never `dependencies`. Everything is bundled at build time via esbuild. CI enforces this with `bun run check:deps`.
67+
68+
When adding a package, always use `bun add -d <package>` (the `-d` flag).
69+
70+
When the `@sentry/api` SDK provides types for an API response, import them directly from `@sentry/api` instead of creating redundant Zod schemas in `src/types/sentry.ts`.
71+
6472
## Rules: Use Bun APIs
6573

6674
**CRITICAL**: This project uses Bun as runtime. Always prefer Bun-native APIs over Node.js equivalents.
@@ -1038,4 +1046,7 @@ mock.module("./some-module", () => ({
10381046
10391047
<!-- lore:019d3f09-d39b-7795-b8a0-4a3e58a996f9 -->
10401048
* **validateWidgetEnums skipDeprecatedCheck for edit-path inherited datasets**: When editing a widget, \`effectiveDataset = flags.dataset ?? existing.widgetType\` may inherit a deprecated type (e.g., \`discover\`). The \`validateWidgetEnums\` deprecation check must be skipped for inherited values — only fire when the user explicitly passes \`--dataset\`. Solution: \`validateWidgetEnums(effectiveDisplay, effectiveDataset, { skipDeprecatedCheck: true })\` in \`edit.ts\`. The cross-validation between display type and dataset still runs on effective values, catching incompatible combos. The deprecation rejection helper \`rejectInvalidDataset()\` is extracted to keep \`validateWidgetEnums\` under Biome's complexity limit of 15.
1049+
1050+
<!-- lore:019d5a00-0000-7000-a000-000000000001 -->
1051+
* **set-commits default mode makes speculative --auto API call by design**: When \`release set-commits\` is called without \`--auto\` or \`--local\`, it tries auto-discovery first and falls back to local git on 400 error. This matches the reference sentry-cli behavior (parity-correct). A per-org negative cache in the \`metadata\` table (\`repos_configured.<org>\` = \`"false"\`, 1-hour TTL) skips the speculative auto call on subsequent runs when no repo integration is configured. The cache clears on successful auto-discovery.
10411052
<!-- End lore-managed section -->

docs/public/.well-known/skills/index.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"references/logs.md",
1414
"references/organizations.md",
1515
"references/projects.md",
16+
"references/release.md",
1617
"references/setup.md",
1718
"references/sourcemap.md",
1819
"references/teams.md",

docs/src/content/docs/commands/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The Sentry CLI provides commands for interacting with various Sentry resources.
1414
| [`dashboard`](./dashboard/) | Manage Sentry dashboards |
1515
| [`org`](./org/) | Work with Sentry organizations |
1616
| [`project`](./project/) | Work with Sentry projects |
17+
| [`release`](./release/) | Work with Sentry releases |
1718
| [`repo`](./repo/) | Work with Sentry repositories |
1819
| [`team`](./team/) | Work with Sentry teams |
1920
| [`issue`](./issue/) | Manage Sentry issues |
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
---
2+
title: release
3+
description: Release commands for the Sentry CLI
4+
---
5+
6+
Work with Sentry releases
7+
8+
## Commands
9+
10+
### `sentry release list <org/project>`
11+
12+
List releases
13+
14+
**Arguments:**
15+
16+
| Argument | Description |
17+
|----------|-------------|
18+
| `<org/project>` | &lt;org&gt;/ (all projects), &lt;org&gt;/&lt;project&gt;, or &lt;project&gt; (search) |
19+
20+
**Options:**
21+
22+
| Option | Description |
23+
|--------|-------------|
24+
| `-n, --limit <limit>` | Maximum number of releases to list (default: "30") |
25+
| `-f, --fresh` | Bypass cache, re-detect projects, and fetch fresh data |
26+
| `-c, --cursor <cursor>` | Navigate pages: "next", "prev", "first" (or raw cursor string) |
27+
28+
### `sentry release view <org/version...>`
29+
30+
View release details
31+
32+
**Arguments:**
33+
34+
| Argument | Description |
35+
|----------|-------------|
36+
| `<org/version...>` | [&lt;org&gt;/]&lt;version&gt; - Release version to view |
37+
38+
**Options:**
39+
40+
| Option | Description |
41+
|--------|-------------|
42+
| `-f, --fresh` | Bypass cache, re-detect projects, and fetch fresh data |
43+
44+
### `sentry release create <org/version...>`
45+
46+
Create a release
47+
48+
**Arguments:**
49+
50+
| Argument | Description |
51+
|----------|-------------|
52+
| `<org/version...>` | [&lt;org&gt;/]&lt;version&gt; - Release version to create |
53+
54+
**Options:**
55+
56+
| Option | Description |
57+
|--------|-------------|
58+
| `-p, --project <project>` | Associate with project(s), comma-separated |
59+
| `--finalize` | Immediately finalize the release (set dateReleased) |
60+
| `--ref <ref>` | Git ref (branch or tag name) |
61+
| `--url <url>` | URL to the release source |
62+
| `-n, --dry-run` | Show what would happen without making changes |
63+
64+
### `sentry release finalize <org/version...>`
65+
66+
Finalize a release
67+
68+
**Arguments:**
69+
70+
| Argument | Description |
71+
|----------|-------------|
72+
| `<org/version...>` | [&lt;org&gt;/]&lt;version&gt; - Release version to finalize |
73+
74+
**Options:**
75+
76+
| Option | Description |
77+
|--------|-------------|
78+
| `--released <released>` | Custom release timestamp (ISO 8601). Defaults to now. |
79+
| `--url <url>` | URL for the release |
80+
| `-n, --dry-run` | Show what would happen without making changes |
81+
82+
### `sentry release delete <org/version...>`
83+
84+
Delete a release
85+
86+
**Arguments:**
87+
88+
| Argument | Description |
89+
|----------|-------------|
90+
| `<org/version...>` | [&lt;org&gt;/]&lt;version&gt; - Release version to delete |
91+
92+
**Options:**
93+
94+
| Option | Description |
95+
|--------|-------------|
96+
| `-y, --yes` | Skip confirmation prompt |
97+
| `-f, --force` | Force the operation without confirmation |
98+
| `-n, --dry-run` | Show what would happen without making changes |
99+
100+
### `sentry release deploy <org/version environment name...>`
101+
102+
Create a deploy for a release
103+
104+
**Arguments:**
105+
106+
| Argument | Description |
107+
|----------|-------------|
108+
| `<org/version environment name...>` | [&lt;org&gt;/]&lt;version&gt; &lt;environment&gt; [name] |
109+
110+
**Options:**
111+
112+
| Option | Description |
113+
|--------|-------------|
114+
| `--url <url>` | URL for the deploy |
115+
| `--started <started>` | Deploy start time (ISO 8601) |
116+
| `--finished <finished>` | Deploy finish time (ISO 8601) |
117+
| `-t, --time <time>` | Deploy duration in seconds (sets started = now - time, finished = now) |
118+
| `-n, --dry-run` | Show what would happen without making changes |
119+
120+
### `sentry release deploys <org/version...>`
121+
122+
List deploys for a release
123+
124+
**Arguments:**
125+
126+
| Argument | Description |
127+
|----------|-------------|
128+
| `<org/version...>` | [&lt;org&gt;/]&lt;version&gt; - Release version |
129+
130+
### `sentry release set-commits <org/version...>`
131+
132+
Set commits for a release
133+
134+
**Arguments:**
135+
136+
| Argument | Description |
137+
|----------|-------------|
138+
| `<org/version...>` | [&lt;org&gt;/]&lt;version&gt; - Release version |
139+
140+
**Options:**
141+
142+
| Option | Description |
143+
|--------|-------------|
144+
| `--auto` | Use repository integration to auto-discover commits |
145+
| `--local` | Read commits from local git history |
146+
| `--clear` | Clear all commits from the release |
147+
| `--commit <commit>` | Explicit commit as REPO@SHA or REPO@PREV..SHA (comma-separated) |
148+
| `--initial-depth <initial-depth>` | Number of commits to read with --local (default: "20") |
149+
150+
### `sentry release propose-version`
151+
152+
Propose a release version
153+
154+
All commands support `--json` for machine-readable output and `--fields` to select specific JSON fields.
155+
156+
<!-- GENERATED:END -->
157+
158+
## Examples
159+
160+
```bash
161+
# List releases (auto-detect org)
162+
sentry release list
163+
164+
# List releases in a specific org
165+
sentry release list my-org/
166+
167+
# View release details
168+
sentry release view 1.0.0
169+
sentry release view my-org/1.0.0
170+
171+
# Create and finalize a release
172+
sentry release create 1.0.0 --finalize
173+
174+
# Create a release, then finalize separately
175+
sentry release create 1.0.0
176+
sentry release set-commits 1.0.0 --auto
177+
sentry release finalize 1.0.0
178+
179+
# Set commits from local git history
180+
sentry release set-commits 1.0.0 --local
181+
182+
# Create a deploy
183+
sentry release deploy 1.0.0 production
184+
sentry release deploy 1.0.0 staging "Deploy #42"
185+
186+
# Propose a version from git HEAD
187+
sentry release create $(sentry release propose-version)
188+
189+
# Output as JSON
190+
sentry release list --json
191+
sentry release view 1.0.0 --json
192+
```

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"type": "git",
66
"url": "git+https://github.com/getsentry/cli.git"
77
},
8+
"main": "./dist/index.cjs",
89
"devDependencies": {
910
"@anthropic-ai/sdk": "^0.39.0",
1011
"@biomejs/biome": "2.3.8",
@@ -43,18 +44,16 @@
4344
"wrap-ansi": "^10.0.0",
4445
"zod": "^3.24.0"
4546
},
46-
"bin": {
47-
"sentry": "./dist/bin.cjs"
48-
},
49-
"main": "./dist/index.cjs",
50-
"types": "./dist/index.d.cts",
5147
"exports": {
5248
".": {
5349
"types": "./dist/index.d.cts",
5450
"require": "./dist/index.cjs",
5551
"default": "./dist/index.cjs"
5652
}
5753
},
54+
"bin": {
55+
"sentry": "./dist/bin.cjs"
56+
},
5857
"description": "Sentry CLI - A command-line interface for using Sentry built by robots and humans for robots and humans",
5958
"engines": {
6059
"node": ">=22"
@@ -94,5 +93,6 @@
9493
"check:deps": "bun run script/check-no-deps.ts",
9594
"check:errors": "bun run script/check-error-patterns.ts"
9695
},
97-
"type": "module"
96+
"type": "module",
97+
"types": "./dist/index.d.cts"
9898
}

plugins/sentry-cli/skills/sentry-cli/SKILL.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,22 @@ Manage Sentry dashboards
313313

314314
→ Full flags and examples: `references/dashboards.md`
315315

316+
### Release
317+
318+
Work with Sentry releases
319+
320+
- `sentry release list <org/project>` — List releases
321+
- `sentry release view <org/version...>` — View release details
322+
- `sentry release create <org/version...>` — Create a release
323+
- `sentry release finalize <org/version...>` — Finalize a release
324+
- `sentry release delete <org/version...>` — Delete a release
325+
- `sentry release deploy <org/version environment name...>` — Create a deploy for a release
326+
- `sentry release deploys <org/version...>` — List deploys for a release
327+
- `sentry release set-commits <org/version...>` — Set commits for a release
328+
- `sentry release propose-version` — Propose a release version
329+
330+
→ Full flags and examples: `references/release.md`
331+
316332
### Repo
317333

318334
Work with Sentry repositories

0 commit comments

Comments
 (0)