Skip to content
Merged
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
70 changes: 70 additions & 0 deletions docs/rfc/0011-github-releases.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# RFC 0011: Auto-create versions from GitHub releases

| Category | Status | Created | Author |
| --- | --- | --- | --- |
| Integrations | <Badge color="gray">Draft</Badge> | 2026-04-20 | Aditya Choudhari |

**Issue:** [#993](https://github.com/ctrlplanedev/ctrlplane/issues/993)

## Problem

How do we know which Ctrlplane deployment a GitHub release belongs to?

`owner/repo` alone fails for monorepos (e.g. `ctrlplanedev/deployments` produces releases for `wandb`, `shared-tenant`, `k8s`, etc.). Naming-convention approaches (`<service>/v<version>`) fight existing tools — release-please emits `<service>-v<ver>`, changesets emits `<pkg>@<ver>`, semantic-release emits `v<ver>`.

## Proposal

A single CEL selector on deployment metadata.

```
git/release-selector: "repository.full_name == 'ctrlplanedev/deployments' && changedPaths.exists(p, p.startsWith('wandb/'))"
```

### Flow

1. `release` webhook hits `apps/api/src/routes/github/release.ts`.
2. Verify signature, resolve installation.
3. Build CEL context.
4. For every deployment with `git/release-selector` in metadata, evaluate.
5. Match → create deployment version with `release.tag_name` as the version.
Comment on lines +25 to +29

### CEL context

```
action: string
release: ReleaseObject // webhook payload
repository: RepositoryObject // webhook payload (topics, custom_properties included)
sender: User // webhook payload

previousTag: string | null // compare API
changedPaths: string[] // compare API
commits: { sha, message, author }[] // compare API
```

Only the bottom block requires an API call (`GET /repos/{o}/{r}/compare/{prev}...{tag}`).

Comment on lines +39 to +45
### Metadata keys

| Key | Required | Purpose |
| --- | --- | --- |
| `git/release-selector` | yes | CEL returning bool |

No `git/repo` key — repo check lives inside the selector.

### GitHub App permissions

- Metadata: Read
- Contents: Read
- Events: `Release`, `Installation`, `Installation repositories`

### Error handling

- Selector throws → log, skip deployment.
- Compare API fails → evaluate without enriched fields (selectors referencing them evaluate false).
- Zero matches → log + 200.

## Out of scope (v1)

- Selector prefilter / indexing
- Draft / prerelease handling beyond what selectors express
- Backfilling historical releases
Loading