feat: add github and linear webhook events#34
Conversation
Greptile SummaryThis PR adds GitHub and Linear webhook event sources to Kelos, introducing a new The overall architecture is well-structured — per-source servers provide fault isolation, idempotency is tracked via a delivery cache, and filter semantics (OR within type, AND across filter fields) are correctly documented and implemented. The signature validation and Linear filter logic in particular are clean and well-tested. Issues found:
Confidence Score: 4/5Three P1 bugs in handler.go should be resolved before merging — concurrency response code, broken owner references, and a delivery-ID race condition. The new feature is architecturally sound and well-tested, but handler.go has three distinct P1 defects that affect observable production behavior: wrong HTTP status for concurrency limits, broken task ownership (garbage collection won't work), and a race condition that can produce duplicate tasks under concurrent retries. These are straightforward to fix but real enough to block merge. internal/webhook/handler.go requires the most attention for all three P1 issues; internal/webhook/github_filter.go has a P2 silent-filter gap for IssuesEvent BodyContains. Important Files Changed
Sequence DiagramsequenceDiagram
participant GH as GitHub/Linear
participant WH as WebhookHandler (ServeHTTP)
participant SIG as signature.go (ValidateSignature)
participant DC as DeliveryCache
participant PW as processWebhook
participant K8S as Kubernetes API (client.List / client.Create)
participant TB as TaskBuilder
GH->>WH: POST /webhook (event headers + body)
WH->>SIG: ValidateGitHubSignature / ValidateLinearSignature
SIG-->>WH: ok / err
WH->>DC: IsProcessed(deliveryID)
DC-->>WH: false (not yet seen)
WH->>PW: processWebhook(eventType, body)
PW->>K8S: client.List(TaskSpawners)
K8S-->>PW: []TaskSpawner
loop For each matching TaskSpawner
PW->>PW: matchesSpawner (MatchesGitHubEvent / MatchesLinearEvent)
PW->>TB: BuildTask(templateVars)
TB-->>PW: *Task
PW->>K8S: client.Create(Task)
end
PW-->>WH: (tasksCreated, nil)
WH->>DC: MarkProcessed(deliveryID)
WH-->>GH: HTTP 200 OK
Reviews (1): Last reviewed commit: "feat: add github and linear webhook even..." | Re-trigger Greptile |
1. P1 - MaxConcurrencyError HTTP Response ✅ Fixed ServeHTTP to properly handle MaxConcurrencyError with HTTP 503 and Retry-After header Previously returned generic HTTP 500, now correctly returns 503 for rate limiting 2. P1 - Empty APIVersion/Kind in Owner References ✅ Fixed owner reference creation to use client.Scheme().ObjectKinds() to get proper GVK Added missing Controller and BlockOwnerDeletion fields for proper garbage collection Previously had empty strings, now has correct API version and kind 3. P1 - TOCTOU Race Condition in Idempotency ✅ Replaced separate IsProcessed()/MarkProcessed() with atomic CheckAndMark() Eliminated race window where two requests with same delivery ID could both pass the check Now uses single lock operation for check-and-set 4. P2 - Invalid Kubernetes Names from Truncation ✅ Fixed task name generation to handle nil/empty ID values safely Added strings.TrimRight(taskName[:63], "-.") to ensure names don't end with invalid characters Prevents server-side validation failures 5. P2 - BodyContains Filter Ignored for IssuesEvent ✅ Fixed GitHub filter to check issue body for BodyContains on IssuesEvent Previously only checked comment body on IssueCommentEvent Now properly filters by issue body content All tests pass and the implementation builds successfully. The webhook system now properly handles rate limiting, ensures atomic idempotency checks, creates valid owner references for garbage collection, generates valid Kubernetes resource names, and correctly applies all filter conditions.
* feat: add github and linear webhook events * fix: build images * Fixed Issues 1. P1 - MaxConcurrencyError HTTP Response ✅ Fixed ServeHTTP to properly handle MaxConcurrencyError with HTTP 503 and Retry-After header Previously returned generic HTTP 500, now correctly returns 503 for rate limiting 2. P1 - Empty APIVersion/Kind in Owner References ✅ Fixed owner reference creation to use client.Scheme().ObjectKinds() to get proper GVK Added missing Controller and BlockOwnerDeletion fields for proper garbage collection Previously had empty strings, now has correct API version and kind 3. P1 - TOCTOU Race Condition in Idempotency ✅ Replaced separate IsProcessed()/MarkProcessed() with atomic CheckAndMark() Eliminated race window where two requests with same delivery ID could both pass the check Now uses single lock operation for check-and-set 4. P2 - Invalid Kubernetes Names from Truncation ✅ Fixed task name generation to handle nil/empty ID values safely Added strings.TrimRight(taskName[:63], "-.") to ensure names don't end with invalid characters Prevents server-side validation failures 5. P2 - BodyContains Filter Ignored for IssuesEvent ✅ Fixed GitHub filter to check issue body for BodyContains on IssuesEvent Previously only checked comment body on IssueCommentEvent Now properly filters by issue body content All tests pass and the implementation builds successfully. The webhook system now properly handles rate limiting, ensures atomic idempotency checks, creates valid owner references for garbage collection, generates valid Kubernetes resource names, and correctly applies all filter conditions. * chore: generate
* feat: add github and linear webhook events * fix: build images * Fixed Issues 1. P1 - MaxConcurrencyError HTTP Response ✅ Fixed ServeHTTP to properly handle MaxConcurrencyError with HTTP 503 and Retry-After header Previously returned generic HTTP 500, now correctly returns 503 for rate limiting 2. P1 - Empty APIVersion/Kind in Owner References ✅ Fixed owner reference creation to use client.Scheme().ObjectKinds() to get proper GVK Added missing Controller and BlockOwnerDeletion fields for proper garbage collection Previously had empty strings, now has correct API version and kind 3. P1 - TOCTOU Race Condition in Idempotency ✅ Replaced separate IsProcessed()/MarkProcessed() with atomic CheckAndMark() Eliminated race window where two requests with same delivery ID could both pass the check Now uses single lock operation for check-and-set 4. P2 - Invalid Kubernetes Names from Truncation ✅ Fixed task name generation to handle nil/empty ID values safely Added strings.TrimRight(taskName[:63], "-.") to ensure names don't end with invalid characters Prevents server-side validation failures 5. P2 - BodyContains Filter Ignored for IssuesEvent ✅ Fixed GitHub filter to check issue body for BodyContains on IssuesEvent Previously only checked comment body on IssueCommentEvent Now properly filters by issue body content All tests pass and the implementation builds successfully. The webhook system now properly handles rate limiting, ensures atomic idempotency checks, creates valid owner references for garbage collection, generates valid Kubernetes resource names, and correctly applies all filter conditions. * chore: generate
* feat: add github and linear webhook events * fix: build images * Fixed Issues 1. P1 - MaxConcurrencyError HTTP Response ✅ Fixed ServeHTTP to properly handle MaxConcurrencyError with HTTP 503 and Retry-After header Previously returned generic HTTP 500, now correctly returns 503 for rate limiting 2. P1 - Empty APIVersion/Kind in Owner References ✅ Fixed owner reference creation to use client.Scheme().ObjectKinds() to get proper GVK Added missing Controller and BlockOwnerDeletion fields for proper garbage collection Previously had empty strings, now has correct API version and kind 3. P1 - TOCTOU Race Condition in Idempotency ✅ Replaced separate IsProcessed()/MarkProcessed() with atomic CheckAndMark() Eliminated race window where two requests with same delivery ID could both pass the check Now uses single lock operation for check-and-set 4. P2 - Invalid Kubernetes Names from Truncation ✅ Fixed task name generation to handle nil/empty ID values safely Added strings.TrimRight(taskName[:63], "-.") to ensure names don't end with invalid characters Prevents server-side validation failures 5. P2 - BodyContains Filter Ignored for IssuesEvent ✅ Fixed GitHub filter to check issue body for BodyContains on IssuesEvent Previously only checked comment body on IssueCommentEvent Now properly filters by issue body content All tests pass and the implementation builds successfully. The webhook system now properly handles rate limiting, ensures atomic idempotency checks, creates valid owner references for garbage collection, generates valid Kubernetes resource names, and correctly applies all filter conditions. * chore: generate
* feat: add github and linear webhook events * fix: build images * Fixed Issues 1. P1 - MaxConcurrencyError HTTP Response ✅ Fixed ServeHTTP to properly handle MaxConcurrencyError with HTTP 503 and Retry-After header Previously returned generic HTTP 500, now correctly returns 503 for rate limiting 2. P1 - Empty APIVersion/Kind in Owner References ✅ Fixed owner reference creation to use client.Scheme().ObjectKinds() to get proper GVK Added missing Controller and BlockOwnerDeletion fields for proper garbage collection Previously had empty strings, now has correct API version and kind 3. P1 - TOCTOU Race Condition in Idempotency ✅ Replaced separate IsProcessed()/MarkProcessed() with atomic CheckAndMark() Eliminated race window where two requests with same delivery ID could both pass the check Now uses single lock operation for check-and-set 4. P2 - Invalid Kubernetes Names from Truncation ✅ Fixed task name generation to handle nil/empty ID values safely Added strings.TrimRight(taskName[:63], "-.") to ensure names don't end with invalid characters Prevents server-side validation failures 5. P2 - BodyContains Filter Ignored for IssuesEvent ✅ Fixed GitHub filter to check issue body for BodyContains on IssuesEvent Previously only checked comment body on IssueCommentEvent Now properly filters by issue body content All tests pass and the implementation builds successfully. The webhook system now properly handles rate limiting, ensures atomic idempotency checks, creates valid owner references for garbage collection, generates valid Kubernetes resource names, and correctly applies all filter conditions. * chore: generate
* feat: add github and linear webhook events * fix: build images * Fixed Issues 1. P1 - MaxConcurrencyError HTTP Response ✅ Fixed ServeHTTP to properly handle MaxConcurrencyError with HTTP 503 and Retry-After header Previously returned generic HTTP 500, now correctly returns 503 for rate limiting 2. P1 - Empty APIVersion/Kind in Owner References ✅ Fixed owner reference creation to use client.Scheme().ObjectKinds() to get proper GVK Added missing Controller and BlockOwnerDeletion fields for proper garbage collection Previously had empty strings, now has correct API version and kind 3. P1 - TOCTOU Race Condition in Idempotency ✅ Replaced separate IsProcessed()/MarkProcessed() with atomic CheckAndMark() Eliminated race window where two requests with same delivery ID could both pass the check Now uses single lock operation for check-and-set 4. P2 - Invalid Kubernetes Names from Truncation ✅ Fixed task name generation to handle nil/empty ID values safely Added strings.TrimRight(taskName[:63], "-.") to ensure names don't end with invalid characters Prevents server-side validation failures 5. P2 - BodyContains Filter Ignored for IssuesEvent ✅ Fixed GitHub filter to check issue body for BodyContains on IssuesEvent Previously only checked comment body on IssueCommentEvent Now properly filters by issue body content All tests pass and the implementation builds successfully. The webhook system now properly handles rate limiting, ensures atomic idempotency checks, creates valid owner references for garbage collection, generates valid Kubernetes resource names, and correctly applies all filter conditions. * chore: generate * fix: normalize the tasknames
What type of PR is this?
What this PR does / why we need it:
Which issue(s) this PR is related to:
Special notes for your reviewer:
Does this PR introduce a user-facing change?