Skip to content

Commit 204352e

Browse files
feat(skills): add sentry-setup-releases over shared releases refs (#309)
An issue tells you what broke but nothing about when it started or what changed, and nothing in the shipped library wires that up. The second skill from the getting-started design's later-focus set, filling that gap. Releases are not only a task someone asks for, though -- like readable stack traces, they are part of getting setup right. first-error-setup.md already tells the agent to make sure release/environment are set in the production environment, and could not act, because no procedure existed to hand off to. So the content lands in references/releases/ and is hydrated by sentry-get-started and sentry-instrument as well, and both of those hooks now route into it. Three consumers, one copy. Why a group rather than a single file: this setup has two halves that fail silently and independently -- the SDK tagging events with a release name, and CI creating a release object under that same name with its commits and deploy. Neither errors when the other is missing, so the group leads with an index.md that diagnoses which half is absent before anything gets configured. tagging.md is the half a setup flow can do inline (one init option); ci-pipeline.md, suspect-commits.md, and troubleshooting.md are the rest, and the skill stays thin orchestration over them. auth-token.md moves out of debug-artifacts/ to the top level. Creating a release is a write to the org needing the same build-time secret as an artifact upload, so it now has two consumer groups, and references may not link across domains -- a releases/ file could not have reached it where it sat. The bundler-plugin release options (create/finalize/inject defaulting true, setCommits defaulting to auto) are verified against @sentry/bundler-plugin-core 4.6.1; sentry-docs does not document them.
1 parent 3182b21 commit 204352e

21 files changed

Lines changed: 752 additions & 15 deletions

File tree

src/SKILL_TREE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Self-contained skills — start here. If you're not sure what the user needs, re
3232
| [`sentry-get-started`](skills/sentry-get-started/SKILL.md) | Guided entry point for using Sentry through your agent. Orients you to your current setup and, for a new project, sets up Sentry end to end with sane defaults — provision a project, install the SDK (errors, tracing, and whatever it enables by default), and confirm real telemetry reaches Sentry. Routes other intents (adding more signals, fixing issues) to the right skill. |
3333
| [`sentry-instrument`](skills/sentry-instrument/SKILL.md) | Instrument an application with Sentry — detect the platform, install and initialize the SDK if needed, and wire up any signal — error monitoring, tracing/performance, logging, metrics, profiling, session replay, user feedback, cron check-ins, and AI/LLM monitoring (agent runs, token cost, and conversations for OpenAI, Anthropic, Vercel AI, LangChain, Google GenAI, Pydantic AI, and Laravel AI). Use to add Sentry to a project or to capture more than errors. |
3434
| [`sentry-otel-exporter-setup`](skills/sentry-otel-exporter-setup/SKILL.md) | Configure the OpenTelemetry Collector with Sentry Exporter for multi-project routing and automatic project creation. Use when setting up OTel with Sentry, configuring collector pipelines for traces and logs, or routing telemetry from multiple services to Sentry projects. |
35+
| [`sentry-setup-releases`](skills/sentry-setup-releases/SKILL.md) | Set up Sentry releases and deploy tracking — tag events with a version and environment, create the release in CI with its commits, and wire up suspect commits and code mappings, so Sentry can show which release introduced an issue, which commit is responsible, and release health. Use when asked to set up releases, track deploys, see what changed, or when issues show an unknown release or no suspect commit. |
3536
| [`sentry-snapshots-cocoa`](skills/sentry-snapshots-cocoa/SKILL.md) | Full Sentry Snapshots setup for Apple/Cocoa projects. Use when asked to "setup SnapshotPreviews", "setup Apple snapshot testing", "upload Apple snapshots to Sentry", "setup Apple snapshot GitHub Actions", or "setup Apple selective snapshot testing". |
3637

3738
## Workflows

src/references/debug-artifacts/auth-token.md renamed to src/references/auth-token.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Auth token (the build-time secret)
22

3-
Uploading artifacts is a *write* to your Sentry org, so it needs an auth token. This is **not the
4-
DSN**: the DSN is public and identifies where events go; the token is a secret and grants upload
5-
rights. Confusing the two is the most common cause of "I configured everything and nothing uploaded."
3+
Anything your build writes *to* your Sentry org needs an auth token — uploading artifacts, and
4+
creating or finalizing releases. This is **not the DSN**: the DSN is public and identifies where
5+
events go; the token is a secret and grants write rights. Confusing the two is the most common cause
6+
of "I configured everything and nothing happened."
67

78
## Which token
89

src/references/debug-artifacts/android.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,6 @@ Gradle-plugin adoption is genuinely blocked, and read the current sentry-cli doc
5858
| Frames still obfuscated after a release build | `autoUploadProguardMapping` not enabled, or minification runs in a variant the plugin doesn't cover | Enable it; confirm the release variant is the one shipped |
5959
| Debug builds look fine, release doesn't | Expected — obfuscation only runs for release | Always test symbolication on a release build |
6060
| Kotlin frames readable, native frames not | Only the mapping was uploaded | Enable native symbol upload |
61-
| Nothing uploads, build green | Token unset — see `auth-token.md` | Add `SENTRY_AUTH_TOKEN` to the build environment |
61+
| Nothing uploads, build green | Token unset — see `../auth-token.md` | Add `SENTRY_AUTH_TOKEN` to the build environment |
6262
| Worked, then stopped after a version bump | Plugin option renamed across a major, now silently ignored | Re-check option names against the plugin docs for your version |
6363
| Frames readable, no code context | `includeSourceContext` off | Enable it and re-check a new event |

src/references/debug-artifacts/dotnet.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ discouraged for exactly that reason.
5151
- **CI:** set `SENTRY_AUTH_TOKEN` from the platform's secret store.
5252
- **Docker:** BuildKit's `--secret`, so the token never persists into an image layer.
5353

54-
Details in [`auth-token.md`](auth-token.md).
54+
Details in [`../auth-token.md`](../auth-token.md).
5555

5656
## Traps
5757

src/references/debug-artifacts/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Two facts that change the answer, worth establishing early:
4545
| .NET — ASP.NET Core, MAUI, WPF, WinForms, Azure Functions | Portable PDB | [`dotnet.md`](dotnet.md) |
4646
| Python, Ruby, PHP, Go, Elixir | Usually none — frames come from readable source | see below |
4747

48-
Every path needs an auth token: [`auth-token.md`](auth-token.md). It is the single most common reason
48+
Every path needs an auth token: [`../auth-token.md`](../auth-token.md). It is the single most common reason
4949
a correct-looking setup uploads nothing.
5050

5151
For the last row, unreadable frames are rarely an artifact problem. Check that the deployed code

src/references/debug-artifacts/javascript.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ already readable and no upload is needed — confirm that before adding a build
8181

8282
| Symptom | Cause | Fix |
8383
|---|---|---|
84-
| Nothing uploaded, build green | Maps not emitted, or token unset | Enable `sourcemap` in the bundler; see `auth-token.md` |
84+
| Nothing uploaded, build green | Maps not emitted, or token unset | Enable `sourcemap` in the bundler; see `../auth-token.md` |
8585
| Uploaded, frames still minified | Upload ran without `inject`, so no Debug IDs | Add `sourcemaps inject` before `upload` |
8686
| Only *some* frames readable | Partial upload — a chunk or the framework's own bundle wasn't included | Widen the upload path / `widenClientFileUpload` |
8787
| Was fine, broke after a deploy | Upload happens after deploy, or a build cache reused stale maps | Move upload into the build, before deploy |

src/references/first-error-setup.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ plainly, but don't take deploy actions without their consent:**
4747
Dockerfile/Procfile, a hosting platform (Vercel, Fly, Render, Heroku, …), or a deploy section in
4848
`AGENTS.md`/`CLAUDE.md` — use it. Walk the user through (or offer to make) the changes to ship the
4949
Sentry-instrumented build, making sure the DSN and the `release`/`environment` values are set in
50-
the production environment, not just locally.
50+
the production environment, not just locally. [`releases/index.md`](releases/index.md) routes to the
51+
per-platform way to set those two — do it here rather than leaving events tagged "unknown release";
52+
it is a one-option change, and every release feature depends on it.
5153
- **Otherwise**, advocate plainly and ask: getting Sentry into production is the most important next
5254
step because real user errors are where the payoff is — how do you deploy this, and can I help wire
5355
it in?
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# Creating the release in CI — commits, finalize, deploy
2+
3+
The SDK half tags events. This half creates the **release object** in Sentry and hangs the useful
4+
metadata off it. Both halves are required; neither works alone.
5+
6+
## Why bother, when Sentry auto-creates releases
7+
8+
Sentry creates a release entity the first time it sees an event carrying an unknown release name. That
9+
auto-created release is a bare label: no commits, so no suspect commits from release data and no
10+
`Fixes` resolution; no finalize timestamp, so "resolve in next release" has nothing to anchor to; no
11+
deploy, so no deploy notifications. Creating it deliberately in CI is what turns the label into the
12+
feature set.
13+
14+
## Where it goes in the pipeline
15+
16+
Order matters, and getting it wrong is the most common reason a correct-looking setup does nothing:
17+
18+
1. **Check out with full git history.** Commit association walks the log between the previous release
19+
and `HEAD`. A shallow clone (CI default) has nothing to walk — on GitHub Actions that means
20+
`fetch-depth: 0`.
21+
2. **Build**, with the release name baked in (see [`tagging.md`](tagging.md)).
22+
3. **Create the release and upload artifacts** — source maps or debug files, from *this* build.
23+
4. **Associate commits.**
24+
5. **Finalize** the release.
25+
6. **Deploy**, then **record the deploy** into its environment.
26+
27+
The rule underneath it: the release step runs **after the build, before the deploy**, and the files you
28+
deploy must be the files that were built when the artifacts were uploaded. Uploading after deploy means
29+
events arrive before Sentry can process them.
30+
31+
Every path below needs an auth token — it's a write to your org. See
32+
[`../auth-token.md`](../auth-token.md); the token, `SENTRY_ORG`, and
33+
`SENTRY_PROJECT` are the same three variables for all of them.
34+
35+
## Path A — a JavaScript bundler plugin (check this first)
36+
37+
If the project already builds with `@sentry/webpack-plugin`, `@sentry/vite-plugin`,
38+
`@sentry/rollup-plugin`, `@sentry/esbuild-plugin`, or a framework SDK that wraps one (`@sentry/nextjs`,
39+
`@sentry/sveltekit`, `@sentry/react-router`, `@sentry/tanstackstart-react`, `@sentry/nuxt`), **most of
40+
this is already happening.** Do not add a parallel `sentry-cli` pipeline next to it — you'll get two
41+
releases fighting over the same name. Configure the plugin instead:
42+
43+
```js
44+
sentryVitePlugin({
45+
org: process.env.SENTRY_ORG,
46+
project: process.env.SENTRY_PROJECT,
47+
authToken: process.env.SENTRY_AUTH_TOKEN,
48+
49+
release: {
50+
// name: defaults to a detected CI value, else the git HEAD SHA
51+
// inject: true — injects the name into the bundle for the SDK
52+
// create: true — creates the release in Sentry
53+
// finalize: true — finalizes when the build ends
54+
// setCommits: { auto: true } — associates commits
55+
deploy: {env: 'production'},
56+
},
57+
})
58+
```
59+
60+
The defaults do the right thing: `inject`, `create`, and `finalize` are all `true`, and `setCommits`
61+
defaults to `{auto: true}`. In practice there are only three things to check:
62+
63+
- **`deploy` is not set by default** — add `deploy: {env: '<environment>'}` to get deploy tracking.
64+
- **Commit association still needs git history in CI.** The plugin shells out to the same logic
65+
`sentry-cli` uses, so `fetch-depth: 0` applies here too.
66+
- `release.name` is unset and undetectable (no git, no recognized CI) → **no release is created at
67+
all**, silently. Set it explicitly in that case.
68+
69+
Useful escapes: `setCommits: {auto: true, ignoreMissing: true}` when history is rewritten by
70+
squash-merges, `setCommits: {repo: 'owner/name', commit: '<sha>'}` when the build has no repo access,
71+
`setCommits: false` to opt out, and `release.vcsRemote` if the remote isn't `origin`.
72+
73+
## Path B — GitHub Actions
74+
75+
For everything that isn't a JS bundler build, `getsentry/action-release` is the shortest correct path.
76+
It creates the release, associates commits, finalizes, and records the deploy in one step:
77+
78+
```yaml
79+
- uses: actions/checkout@v4
80+
with:
81+
fetch-depth: 0 # required: commit association needs history
82+
83+
# ... your build steps here ...
84+
85+
- name: Create Sentry release
86+
uses: getsentry/action-release@v3
87+
env:
88+
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
89+
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
90+
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
91+
with:
92+
environment: production
93+
release: ${{ github.sha }} # optional; defaults to the triggering commit SHA
94+
sourcemaps: ./dist # only if you have JS source maps to upload
95+
```
96+
97+
The action's default release name is the GitHub SHA that triggered the workflow — fine, as long as the
98+
SDK tags the same value. Wire `SENTRY_RELEASE=${{ github.sha }}` into the build step if you rely on
99+
that default.
100+
101+
Self-hosted Sentry also needs `SENTRY_URL`.
102+
103+
## Path C — `sentry-cli`, any CI
104+
105+
The explicit form. Use it for CI providers without an integration, for mobile builds, and whenever you
106+
need to see each step:
107+
108+
```bash
109+
export SENTRY_AUTH_TOKEN=... # from CI secrets
110+
export SENTRY_ORG=my-org
111+
export SENTRY_PROJECT=my-project
112+
113+
VERSION=$(sentry-cli releases propose-version) # or your own version string
114+
115+
sentry-cli releases new "$VERSION"
116+
117+
# ... build, and upload source maps / debug files for this build ...
118+
119+
sentry-cli releases set-commits "$VERSION" --auto
120+
sentry-cli releases finalize "$VERSION"
121+
122+
# ... deploy ...
123+
124+
sentry-cli deploys new --release "$VERSION" -e production
125+
```
126+
127+
Notes on the individual steps:
128+
129+
- **`new`** takes multiple projects when a release spans them: `-p project1 -p project2`. Remember
130+
releases are org-global — prefix the version accordingly.
131+
- **`--finalize`** on `new` collapses steps 1 and 5 if you don't need the window in between. Finalizing
132+
separately, at deploy time, is more accurate: the finalize timestamp is what "the next release" means
133+
when resolving issues, and it's the base release for `--auto` commit association.
134+
- **`set-commits --auto`** discovers the repo from the working directory and associates everything
135+
between the previous release's head commit and the current `HEAD`. With no SCM integration installed
136+
it falls back to the local git tree (the last 10–20 commits on a first release, tunable with
137+
`--initial-depth`); `--local` makes that fallback the explicit default.
138+
- When the build can't reach the repo, name the commits: `--commit "owner/repo@<sha>"`, repeated per
139+
repo, or a range `--commit "owner/repo@<prev>..<current>"`. The repo name must match what it's called
140+
in Sentry — `sentry-cli repos list` prints the valid names.
141+
- **`--ignore-missing`** rescues `set-commits` when a commit from the previous release no longer exists
142+
(amend, rebase, squash-merge, force-push). It falls back to the default commit count instead of
143+
failing the build.
144+
- **`deploys new`** accepts `-t <seconds>` to record how long the deploy took, and `deploys list
145+
--release "$VERSION"` to read them back. Deploys can't be deleted.
146+
147+
### Sending commit metadata without the CLI
148+
149+
When the deploy environment can't run `sentry-cli` at all, POST the commits with the release. This is
150+
also the path for orgs that won't connect an SCM integration:
151+
152+
```bash
153+
curl https://sentry.io/api/0/organizations/<org>/releases/ \
154+
-X POST \
155+
-H "Authorization: Bearer $SENTRY_AUTH_TOKEN" \
156+
-H 'Content-Type: application/json' \
157+
-d '{
158+
"version": "2.0rc2",
159+
"projects": ["project-1"],
160+
"commits": [{
161+
"id": "8371445ab8a9facd271df17038ff295a48accae7",
162+
"repository": "owner-name/repo-name",
163+
"author_name": "Author Name",
164+
"author_email": "author@example.com",
165+
"timestamp": "2018-09-20T11:50:22+03:00",
166+
"message": "This is the commit message.",
167+
"patch_set": [
168+
{"path": "path/to/added-file.html", "type": "A"},
169+
{"path": "path/to/modified-file.html", "type": "M"},
170+
{"path": "path/to/deleted-file.html", "type": "D"}
171+
]
172+
}]
173+
}'
174+
```
175+
176+
Two fields carry the weight: **`patch_set`** (types `A`dd, `M`odify, `D`elete) is what powers suspect
177+
commits and suggested assignees — omit it and you get a commit list and nothing else — and
178+
**`author_email`** is what makes the suggested assignee resolvable. `timestamp` controls ordering;
179+
without it, commits stay in the order given.
180+
181+
## Path D — mobile and Flutter
182+
183+
The SDK build plugins on these platforms upload **artifacts** but do not manage releases, with one
184+
exception:
185+
186+
| Platform | Release object |
187+
|---|---|
188+
| Flutter / Dart | `sentry_dart_plugin` handles it — `release` defaults to `name@version` from `pubspec.yaml` and `commits` defaults to `auto`. `ignore_missing: true` is available for rewritten history. |
189+
| Android | The Gradle plugin uploads mappings and source bundles only. Create the release from CI with Path B or C, using the same `packageName@versionName+versionCode` string the SDK tags. |
190+
| Apple / Cocoa | The Xcode build phase uploads dSYMs only. Same: create the release from CI. |
191+
| React Native | The bundled `sentry-cli` build integration creates the release for default names. Custom `release`/`dist` values break it — then it's Path C, plus manual source map upload. |
192+
193+
## Related
194+
195+
- [`tagging.md`](tagging.md) — the name both halves must agree on.
196+
- [`suspect-commits.md`](suspect-commits.md) — what commit association actually unlocks.
197+
- [`troubleshooting.md`](troubleshooting.md) — when the pipeline runs but Sentry shows nothing.

0 commit comments

Comments
 (0)