Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a4d51a4
Add GitLab support for Seer Autofix
dnplkndll Jan 29, 2026
d7e0a63
feat: Add build infrastructure for custom Kencove images
dnplkndll Jan 30, 2026
16f22ad
fix: Patch Seer URLs before server import for self-hosted
dnplkndll Jan 30, 2026
a303575
fix: Patch Seer URLs in server.py during Docker build
dnplkndll Jan 30, 2026
ffc2574
fix: Use # as sed delimiter to avoid escaping issues
dnplkndll Jan 30, 2026
7b2d8c6
feat: Disable seer-explorer feature flag for self-hosted
dnplkndll Jan 31, 2026
ba60387
fix(ci): Fix Cloud Build workflow — correct WIF, SA, branch, and use …
dnplkndll Feb 18, 2026
df0ecc8
fix(build): scope gcloudignore patterns to root directories
dnplkndll Feb 18, 2026
b660ef8
fix(build): remove uwsgi-dogstatsd plugin and upgrade uv to 0.9.28
dnplkndll Feb 18, 2026
a20e5fd
fix(seer): Make org_id and integration_id optional in preferences ser…
dnplkndll Feb 18, 2026
abae6d8
ci(sentry): add GHCR public build workflow (#1)
dnplkndll May 18, 2026
9dc3c3e
fix(build): COPY patches/ before pnpm install for 26.5.0 lockfile
dnplkndll May 20, 2026
c81a1e7
ci: hourly sync of master from upstream getsentry/sentry
dnplkndll May 20, 2026
59aea0e
Revert ci: per-repo sync-upstream — handled at org level
dnplkndll May 20, 2026
50354e0
fix(build): bump node 22 -> 24 and pnpm 9 -> 10 for 26.5.0 lockfile
dnplkndll May 20, 2026
7222a8f
fix(build): use upstream getsentry/sentry:26.5.0 as base — drop in-im…
dnplkndll May 20, 2026
d975909
rename: kencove suffix -> ledoent across image tag, branch, Dockerfile
dnplkndll May 20, 2026
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
13 changes: 13 additions & 0 deletions .gcloudignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Cloud Build source upload ignore
# gcloud builds submit uses this to exclude files from the source tarball
.devenv/
.git/
.venv/
.webpack_cache/
node_modules/
/fixtures/
/tests/
*.pyc
__pycache__/
.coverage*
.DS_Store
55 changes: 55 additions & 0 deletions .github/workflows/build-kencove.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Build Kencove Sentry Image

on:
push:
branches:
- '*-kencove'
workflow_dispatch:
inputs:
tag:
description: 'Image tag override (default: commit SHA)'
required: false
default: ''

env:
REGION: us-central1
PROJECT_ID: kencove-prod
REPOSITORY: kencove-docker-repo
IMAGE_NAME: sentry

jobs:
build:
runs-on: ubuntu-24.04
permissions:
contents: read
id-token: write # For Workload Identity Federation

steps:
- uses: actions/checkout@v4

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/103143301688/locations/global/workloadIdentityPools/github-wlif/providers/github-oidc'
service_account: 'github-actions-seer@kencove-prod.iam.gserviceaccount.com'

- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v2

- name: Determine image tag
id: tag
run: |
if [ -n "${{ github.event.inputs.tag }}" ]; then
echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
else
echo "tag=${{ github.sha }}" >> $GITHUB_OUTPUT
fi

- name: Submit Cloud Build
run: |
gcloud builds submit \
--config=cloudbuild.yaml \
--substitutions=COMMIT_SHA=${{ github.sha }},_TAG=${{ steps.tag.outputs.tag }} \
--project=${{ env.PROJECT_ID }} \
--async \
.
127 changes: 127 additions & 0 deletions .github/workflows/build-push-ghcr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: Build and Push Sentry to GHCR (Public)

# Publishes a public ledoent fork of self-hosted Sentry to GitHub Container Registry.
# Companion to build-kencove.yml (kencove-prod GCP Cloud Build, legacy). This workflow exists
# so self-hosted Sentry+Seer deployments can pull without GCP credentials.

on:
push:
branches:
- '*-ledoent'
- feat/build-push-ghcr
tags:
- 'v*-ledoent'
workflow_dispatch:
inputs:
tag:
description: 'Image tag override (default: branch-shortsha)'
required: false
type: string

env:
REGISTRY: ghcr.io
IMAGE_NAME: ledoent/sentry

# Cancel any in-flight build for the same ref when a new push lands. Saves
# ~30 GHA minutes per force-push and prevents two builds from racing to
# publish the same mutable tag.
concurrency:
group: build-push-ghcr-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-push:
# ubuntu-latest is 16 GB RAM on GitHub-hosted free runners as of 2026
# (was 7 GB before the 2024 Larger Hosted Runners refresh). The Sentry
# frontend build uses NODE_OPTIONS=4096 in Dockerfile.ledoent; our first
# build completed in ~12 min on this runner. Bump to ubuntu-latest-large
# (32 GB) only if webpack starts OOMing.
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # full history so sentry.build can derive the version

- name: Free disk space
run: |
# Sentry's build is disk-hungry; trim runner-preinstalled toolchains
# we don't use. Each rm is best-effort against a wildcard so a path
# being absent doesn't fail the build — but we keep the *step* exit
# status real so an unrelated failure (e.g., the docker prune) still
# surfaces.
df -h /
for path in /usr/share/dotnet /usr/local/lib/android /opt/ghc \
/opt/hostedtoolcache/CodeQL; do
[ -e "$path" ] && sudo rm -rf "$path"
done
sudo docker image prune -af
df -h /

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate image tags
id: tags
env:
INPUT_TAG: ${{ inputs.tag }}
REF_NAME: ${{ github.ref_name }}
run: |
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
SHA_SHORT=$(git rev-parse --short HEAD)
BRANCH_TAG=$(echo "$REF_NAME" | sed 's|[^a-zA-Z0-9._-]|-|g' | cut -c1-60)

TAGS="${IMAGE}:${BRANCH_TAG}-${SHA_SHORT}"
TAGS="${TAGS},${IMAGE}:${BRANCH_TAG}"

if [ -n "$INPUT_TAG" ]; then
TAGS="${TAGS},${IMAGE}:${INPUT_TAG}"
fi

if [ "${{ github.ref_type }}" = "tag" ]; then
TAGS="${TAGS},${IMAGE}:${REF_NAME}"
fi

{
echo "tags=${TAGS}"
echo "sha_short=${SHA_SHORT}"
} >> "$GITHUB_OUTPUT"
echo "Generated tags: ${TAGS}"

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
file: self-hosted/Dockerfile.ledoent
platforms: linux/amd64
push: true
tags: ${{ steps.tags.outputs.tags }}
build-args: |
SOURCE_COMMIT=${{ github.sha }}
labels: |
org.opencontainers.image.source=https://github.com/ledoent/sentry
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.title=Sentry (ledoent fork)
org.opencontainers.image.description=Self-hosted Sentry with SEER_URL env support
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Summary
run: |
{
echo "### Sentry pushed to GHCR :rocket:"
echo ""
echo "**Tags:**"
echo '${{ steps.tags.outputs.tags }}' | tr ',' '\n' | sed 's/^/- `/' | sed 's/$/`/'
} >> "$GITHUB_STEP_SUMMARY"
50 changes: 50 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,53 @@ Frontend (`static/`) and backend (`src/`, `tests/`) are **not atomically deploye
- If your changes touch both frontend and backend, split them into **separate PRs**.
- Land the backend PR first when the frontend depends on new API changes.
- Pure test additions alongside `src/` changes are fine in one PR.

## Kencove Fork

This is **ledoent/sentry** (originally kencove/sentry) — a fork of getsentry/sentry with custom modifications for our self-hosted deployment.

### Key Modifications

1. **GitLab Autofix Support** (`static/app/components/events/autofix/utils.tsx`)
- Added `'gitlab'` and `'integrations:gitlab'` to `supportedProviders` array
- Enables GitLab repositories for Seer Autofix feature
- Note: upstream 26.5.0 added GitLab support behind `organizations:seer-gitlab-support` feature flag; this patch may be redundant. Verify before keeping in future rebases.

2. **Seer endpoints serializer** (`src/sentry/seer/endpoints/project_seer_preferences.py`)
- Make `org_id` and `integration_id` optional to handle Seer's repo format differences for self-hosted single-org deployments

3. **Cursor / GitLab integration** (`src/sentry/integrations/cursor/*`)
- Custom GitLab repo URL builder + webhook handler

4. **Self-hosted SEER_URL patch** (`self-hosted/Dockerfile.patch`)
- Patches `server.py` during Docker build so `SEER_DEFAULT_URL` defaults to the in-cluster seer service

### Building Custom Image

The primary build now ships to GHCR:

```bash
# Via the GHCR workflow (default — produces ghcr.io/ledoent/sentry:<branch>-kencove)
gh workflow run build-push-ghcr.yml --ref <branch>-kencove

# Legacy Cloud Build path (kencove-prod GCP, still works)
gcloud builds submit --config=cloudbuild.yaml .
```

### Syncing with Upstream

```bash
git fetch upstream <new-version>
git checkout -b <new-version>-kencove <new-version>
git log --reverse --format=%H <prev-version>..origin/<prev-version>-kencove \
| xargs -I{} git cherry-pick {}
# Resolve conflicts (typically in src/sentry/seer/autofix/coding_agent.py
# and src/sentry/seer/endpoints/project_seer_preferences.py)
git push origin <new-version>-kencove
```

### Related Repositories

- **Seer AI Service**: [ledoent/seer](https://github.com/ledoent/seer) - GitLab repository client
- **Helm Charts**: [kencove/charts](https://github.com/kencove/charts) - Deployment configuration
- **Infra Clusters**: `~/projects/ledoent/infra/deployments/` - Deployment values
75 changes: 75 additions & 0 deletions build-and-push.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/bin/bash
# Build and push Kencove Sentry image to Google Artifact Registry
#
# Usage:
# ./build-and-push.sh # Build with commit SHA tag
# ./build-and-push.sh v26.1.0-gitlab # Build with custom tag
# ./build-and-push.sh --local # Build locally without pushing

set -euo pipefail

# Configuration
PROJECT_ID="${PROJECT_ID:-kencove-prod}"
REGION="${REGION:-us-central1}"
REPOSITORY="${REPOSITORY:-kencove-docker-repo}"
IMAGE_NAME="${IMAGE_NAME:-sentry}"
TAG="${1:-$(git rev-parse --short HEAD)}"

FULL_IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMAGE_NAME}"

echo "=== Kencove Sentry Build ==="
echo "Image: ${FULL_IMAGE}:${TAG}"
echo "Commit: $(git rev-parse HEAD)"
echo ""

if [[ "${1:-}" == "--local" ]]; then
echo "Building locally (not pushing)..."
docker build \
-t "${IMAGE_NAME}:${TAG}" \
-t "${IMAGE_NAME}:latest" \
-f self-hosted/Dockerfile.kencove \
--build-arg SOURCE_COMMIT="$(git rev-parse HEAD)" \
--progress=plain \
.
echo ""
echo "Local build complete: ${IMAGE_NAME}:${TAG}"
exit 0
fi

# Check if using Cloud Build or local Docker
if command -v gcloud &> /dev/null && [[ "${USE_CLOUD_BUILD:-true}" == "true" ]]; then
echo "Using Cloud Build..."
gcloud builds submit \
--config=cloudbuild.yaml \
--substitutions="_TAG=${TAG}" \
--project="${PROJECT_ID}" \
.
else
echo "Using local Docker build + push..."

# Configure Docker for GAR
gcloud auth configure-docker "${REGION}-docker.pkg.dev" --quiet

# Build
docker build \
-t "${FULL_IMAGE}:${TAG}" \
-t "${FULL_IMAGE}:latest" \
-f self-hosted/Dockerfile.kencove \
--build-arg SOURCE_COMMIT="$(git rev-parse HEAD)" \
--progress=plain \
.

# Push
docker push "${FULL_IMAGE}:${TAG}"
docker push "${FULL_IMAGE}:latest"
fi

echo ""
echo "=== Build Complete ==="
echo "Image: ${FULL_IMAGE}:${TAG}"
echo ""
echo "To use in Helm values:"
echo " images:"
echo " sentry:"
echo " repository: ${FULL_IMAGE}"
echo " tag: \"${TAG}\""
57 changes: 57 additions & 0 deletions cloudbuild.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Cloud Build configuration for kencove/sentry
# Builds custom Sentry image with GitLab Autofix support
#
# Usage:
# gcloud builds submit --config=cloudbuild.yaml .
#
# Or with custom tag:
# gcloud builds submit --config=cloudbuild.yaml --substitutions=_TAG=v26.1.0-gitlab .
#
# Trigger on push:
# gcloud builds triggers create github \
# --repo-name=sentry --repo-owner=kencove \
# --branch-pattern="^master$" \
# --build-config=cloudbuild.yaml

substitutions:
_REGION: us-central1
_REPOSITORY: kencove-docker-repo
_IMAGE_NAME: sentry
_TAG: ${COMMIT_SHA}

options:
machineType: E2_HIGHCPU_32
diskSizeGb: 200
logging: CLOUD_LOGGING_ONLY

steps:
# Build using multi-stage Dockerfile that handles both frontend and runtime
- name: 'gcr.io/cloud-builders/docker'
id: 'build-image'
args:
- 'build'
- '-t'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE_NAME}:${_TAG}'
- '-t'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE_NAME}:latest'
- '-f'
- 'self-hosted/Dockerfile.kencove'
- '--build-arg'
- 'SOURCE_COMMIT=${COMMIT_SHA}'
- '--progress=plain'
- '.'

# Push to Artifact Registry
- name: 'gcr.io/cloud-builders/docker'
id: 'push-image'
args:
- 'push'
- '--all-tags'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE_NAME}'
waitFor: ['build-image']

images:
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE_NAME}:${_TAG}'
- '${_REGION}-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE_NAME}:latest'

timeout: 3600s # 1 hour - Sentry build is slow
Loading
Loading