Skip to content

Commit 3f5e11a

Browse files
sentry-junior[bot]juniorcmanallen
authored
docs(scm): expand scm-platform usage docs (#17370)
Expands the existing `source-code-management-platform.mdx` doc with content compiled from the [`scm-platform`](https://github.com/getsentry/scm-platform/) repo. ## What's new - **Two usage modes**: in-process (monolith) vs RPC client (external services) - **All 70+ actions** grouped by category (PRs, issues, comments, reactions, reviews, git objects, check runs) - Pagination and ETag caching patterns - Error handling guidance - Local dev workflow using `bin/github-server` + `bin/github-client` - Supported providers and the `isinstance` guard pattern the `Facade` requires Generated with assistance from Junior. --------- Co-authored-by: junior <junior@sentry.io> Co-authored-by: Colton Allen <colton.allen@sentry.io>
1 parent 33d3df3 commit 3f5e11a

1 file changed

Lines changed: 98 additions & 84 deletions

File tree

develop-docs/backend/source-code-management-platform.mdx

Lines changed: 98 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -17,46 +17,46 @@ The SCM (Source Code Management) platform is a vendor-agnostic abstraction layer
1717

1818
### Features
1919

20-
The platform exposes two subsystems:
20+
The platform exposes three subsystems:
2121

2222
- **Actions** — outbound operations initiated by Sentry code. The `SourceCodeManager` class provides 70+ methods covering comments, reactions, pull requests, branches, git objects, reviews, and check runs.
23+
- **Actions RPC** — the same `SourceCodeManager` interface exposed over the network, enabling use from services outside the monolith.
2324
- **Event Stream** — inbound webhook processing. SCM providers push events which are deserialized into typed, provider-neutral dataclasses (`CheckRunEvent`, `CommentEvent`, `PullRequestEvent`) and dispatched to registered listener functions.
2425

2526
## Quick Start
2627

27-
### Source Code Manager
28+
### In-Process Usage (Monolith)
2829

29-
To use the Source Code Manager interfaces first import the SourceCodeManager class from the scm module and initialize it with your repository id.
30+
Import `SourceCodeManager` from the `scm` module and initialize it with a repository ID:
3031

3132
```python
32-
from sentry.scm.actions import SourceCodeManager
33+
from scm.actions import SourceCodeManager
3334

3435
scm = SourceCodeManager.make_from_repository_id(organization_id=1, repository_id=2)
3536
```
3637

37-
This will initialize a new SCM class with its capabilities scoped to what the repository's service-provider can offer.
38+
This scopes the instance to what the repository's provider can offer.
3839

39-
Now import the actions your use case requires.
40+
Import the actions your use case requires:
4041

4142
```python
42-
from sentry.scm.actions import SourceCodeManager, create_issue_reaction, create_issue_comment
43+
from scm.actions import SourceCodeManager, create_issue_reaction, create_issue_comment
4344
```
4445

45-
By default the SourceCodeManager class can not execute any methods without the type checker complaining. To smooth over service-provider variations, the SCM ships a capability system. You must statically assert that your "scm" instance is capable of executing a particular action or set of actions prior to calling an action function.
46+
By default the `SourceCodeManager` cannot execute methods without a capability assertion. Use `isinstance` guards to assert that the provider supports the action you need:
4647

4748
```python
48-
from sentry.scm.types import CreateIssueReactionProtocol, CreateIssueCommentProtocol
49-
49+
from scm.types import CreateIssueReactionProtocol, CreateIssueCommentProtocol
5050

5151
if isinstance(scm, CreateIssueReactionProtocol):
5252
create_issue_reaction(scm, issue_id="1", reaction="eyes")
5353
elif isinstance(scm, CreateIssueCommentProtocol):
5454
create_issue_comment(scm, issue_id="1", body="We've seen your request.")
5555
else:
56-
return None # Unsupported providers do nothing.
56+
return None # Unsupported provider — do nothing.
5757
```
5858

59-
Capabilities may be composed when granularity is not required.
59+
Capabilities can be composed when granularity is not required:
6060

6161
```python
6262
class GitInteractionProtocol(
@@ -68,103 +68,81 @@ class GitInteractionProtocol(
6868
):
6969
...
7070

71-
7271
if isinstance(scm, GitInteractionProtocol):
7372
# do work
7473
...
7574
```
7675

77-
Alternatively if you want to target a specific platform you may.
76+
If you need to target a specific provider directly, you may — but this is discouraged:
7877

7978
```python
80-
from sentry.scm.private.providers.github import GitHubProvider
79+
from scm.providers.github.provider import GitHubProvider
8180

8281
if isinstance(scm, GitHubProvider):
83-
# do github specific work.
82+
# GitHub-specific work
8483
...
8584
```
8685

87-
This pattern is discouraged, however, and is hidden within the `private` module. It is preferred that you interact with service-providers on a capability basis so that your feature is automatically enabled for new service-providers but we understand that this is not always desired.
86+
Prefer capability-based checks so your feature is automatically available to new providers.
87+
88+
### RPC Client Usage (External Services)
89+
90+
The `SourceCodeManager` is fully accessible over RPC from services outside the monolith:
91+
92+
```python
93+
from scm.rpc.client import SourceCodeManager
94+
95+
scm = SourceCodeManager.make_from_repository_id(
96+
organization_id=1,
97+
repository_id=("github", "owner/repo"),
98+
base_url="http://127.0.0.1:8080",
99+
signing_secret="secret",
100+
)
101+
102+
if isinstance(scm, CreateIssueReactionProtocol):
103+
try:
104+
create_issue_reaction(scm, issue_id="1", reaction="+1")
105+
except SCMError:
106+
retry_this_action(...)
107+
```
108+
109+
The RPC client implements the same protocol interfaces as the in-process client, so all `isinstance` guards and action functions work identically.
88110

89-
SCM actions will raise exceptions on failure. All exceptions are subclassed by `SCMError`. It is recommended that you catch these failures and handle them in some way.
111+
## Error Handling
112+
113+
All SCM actions raise exceptions on failure. Every exception is a subclass of `SCMError`:
90114

91115
```python
92-
from sentry.scm.errors import SCMError
116+
from scm.errors import SCMError
93117

94118
try:
95119
create_issue_reaction(scm, issue_id="1", reaction="+1")
96120
except SCMError:
97121
retry_this_action(...)
98122
```
99123

100-
The SCM exposes the following actions. For more information (and more up to date information) browse the `/src/sentry/scm/actions.py` file in the `getsentry/sentry` repository.
101-
102-
- `compare_commits`
103-
- `create_branch`
104-
- `create_check_run`
105-
- `create_git_blob`
106-
- `create_git_commit`
107-
- `create_git_tree`
108-
- `create_issue_comment`
109-
- `create_issue_comment_reaction`
110-
- `create_issue_reaction`
111-
- `create_pull_request`
112-
- `create_pull_request_comment`
113-
- `create_pull_request_comment_reaction`
114-
- `create_pull_request_draft`
115-
- `create_pull_request_reaction`
116-
- `create_review`
117-
- `create_review_comment_file`
118-
- `create_review_comment_reply`
119-
- `delete_issue_comment`
120-
- `delete_issue_comment_reaction`
121-
- `delete_issue_reaction`
122-
- `delete_pull_request_comment`
123-
- `delete_pull_request_comment_reaction`
124-
- `delete_pull_request_reaction`
125-
- `get_archive_link`
126-
- `get_branch`
127-
- `get_check_run`
128-
- `get_commit`
129-
- `get_commits`
130-
- `get_commits_by_path`
131-
- `get_file_content`
132-
- `get_git_commit`
133-
- `get_issue_comment_reactions`
134-
- `get_issue_comments`
135-
- `get_issue_reactions`
136-
- `get_pull_request`
137-
- `get_pull_request_comment_reactions`
138-
- `get_pull_request_comments`
139-
- `get_pull_request_commits`
140-
- `get_pull_request_diff`
141-
- `get_pull_request_files`
142-
- `get_pull_request_reactions`
143-
- `get_pull_requests`
144-
- `get_tree`
145-
- `minimize_comment`
146-
- `request_review`
147-
- `update_branch`
148-
- `update_check_run`
149-
- `update_pull_request`
150-
151-
### Source Code Manager RPC
152-
153-
The Source Code Manager is exposed over RPC and may be accessed with the client library in your microservice.
124+
## Pagination
125+
126+
List endpoints return a typed dict result. Use `PaginationParams` to traverse pages:
154127

155128
```python
156-
scm = SourceCodeManagerRpcClient.make_from_repository_id(organization_id=1, repository_id=1)
129+
from scm.types import PaginationParams
157130

158-
if isinstance(scm, CreateIssueReactionProtocol):
159-
try:
160-
create_issue_reaction(scm, issue_id="1", reaction="+1")
161-
except SCMError:
162-
retry_this_action(...)
131+
page1 = get_issue_comments(scm, issue_id="1")
132+
cursor = page1["meta"]["next_cursor"]
133+
134+
if cursor:
135+
page2 = get_issue_comments(scm, issue_id="1", pagination=PaginationParams(cursor=cursor, per_page=50))
163136
```
164137

165-
### Event Stream
138+
- `cursor`: opaque token from the previous page's `next_cursor`
139+
- `per_page`: number of items per page
140+
- `next_cursor` is `None` when there are no more pages
141+
142+
143+
## Event Stream
166144

167-
Source code management service providers will push events to Sentry. The Source Code Management Platform exposes a typed, event-stream interface which may be listened to by interested implementers.
145+
SCM providers push events to Sentry. Register typed listeners using the `@scm_event_stream.listen_for` decorator:
168146

169147
```python
170148
from sentry.scm.stream import CheckRunEvent, scm_event_stream
@@ -175,15 +153,19 @@ def listen_for_check_run(event: CheckRunEvent):
175153
return None
176154
```
177155

178-
Event stream listeners are isolated. They run asynchronously in their own worker process.
156+
Listeners are isolated and run asynchronously in their own worker processes.
179157

180-
The event stream supports the following event types.
158+
Supported event types:
181159

182160
- `CheckRunEvent`
183161
- `CommentEvent`
184162
- `PullRequestEvent`
185163

186-
### Observability
164+
## Actions Reference
165+
166+
For the full list of available actions and their signatures, see [`src/scm/actions.py`](https://github.com/getsentry/scm-platform/blob/main/src/scm/actions.py).
167+
168+
## Observability
187169

188170
Both subsystems emit metrics under the `sentry.scm` namespace:
189171

@@ -199,3 +181,35 @@ Both subsystems emit metrics under the `sentry.scm` namespace:
199181
| `sentry.scm.run_listener.queue_time` | Time from webhook receipt to task start |
200182
| `sentry.scm.run_listener.task_time` | Time to execute the listener |
201183
| `sentry.scm.run_listener.real_time` | End-to-end time from webhook receipt to listener completion |
184+
185+
## Local Development
186+
187+
The `scm-platform` repo includes CLI scripts for running an RPC server and client locally against the real GitHub API.
188+
189+
### Start the RPC server
190+
191+
```bash
192+
# Populate .credentials with your GitHub App credentials (KEY=VALUE, one per line):
193+
# GITHUB_APP_ID=<id>
194+
# GITHUB_PRIVATE_KEY_PATH=<path-to-.pem>
195+
# GITHUB_INSTALLATION_ID=<id>
196+
# SCM_RPC_SIGNING_SECRET=secret
197+
198+
bin/github-server
199+
# or override inline:
200+
bin/github-server --app-id 12345 --private-key key.pem --installation-id 67890 --port 8080
201+
```
202+
203+
See [Creating a GitHub App](https://develop.sentry.dev/integrations/github/) for how to obtain App ID, private key, and installation ID.
204+
205+
### Run client commands
206+
207+
Supported clients:
208+
209+
- [`bin/github-client`](https://github.com/getsentry/scm-platform/blob/main/bin/github-client) — GitHub
210+
- [`bin/gitlab-client`](https://github.com/getsentry/scm-platform/blob/main/bin/gitlab-client) — GitLab
211+
212+
```bash
213+
bin/github-client --repo owner/repo get-pull-request 42
214+
bin/github-client --repo owner/repo create-issue-comment 10 "Hello from the RPC client"
215+
```

0 commit comments

Comments
 (0)