Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Image configuration
REGISTRY ?= ghcr.io/kelos-dev
VERSION ?= latest
IMAGE_DIRS ?= cmd/kelos-controller cmd/kelos-spawner cmd/kelos-token-refresher cmd/ghproxy claude-code codex gemini opencode cursor
IMAGE_DIRS ?= cmd/kelos-controller cmd/kelos-spawner cmd/kelos-token-refresher cmd/kelos-webhook-server cmd/ghproxy claude-code codex gemini opencode cursor

# Version injection for the kelos CLI – only set ldflags when an explicit
# version is given so that dev builds fall through to runtime/debug info.
Expand Down
101 changes: 100 additions & 1 deletion api/v1alpha1/taskspawner_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ type When struct {
// Jira discovers issues from a Jira project.
// +optional
Jira *Jira `json:"jira,omitempty"`

// GitHubWebhook triggers task spawning on GitHub webhook events.
// +optional
GitHubWebhook *GitHubWebhook `json:"githubWebhook,omitempty"`

// LinearWebhook triggers task spawning on Linear webhook events.
// +optional
LinearWebhook *LinearWebhook `json:"linearWebhook,omitempty"`
}

// Cron triggers task spawning on a cron schedule.
Expand Down Expand Up @@ -295,6 +303,93 @@ type Jira struct {
PollInterval string `json:"pollInterval,omitempty"`
}

// GitHubWebhook configures webhook-driven task spawning from GitHub events.
type GitHubWebhook struct {
// Events is the list of GitHub event types to listen for.
// e.g., "issue_comment", "pull_request_review", "push", "issues"
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinItems=1
Events []string `json:"events"`

// Filters refine which events trigger tasks. If multiple filters match
// the same event type, any match triggers a task (OR semantics).
// If empty, all events in the Events list trigger tasks.
// +optional
Filters []GitHubWebhookFilter `json:"filters,omitempty"`
}

// GitHubWebhookFilter defines filtering criteria for GitHub webhook events.
type GitHubWebhookFilter struct {
// Event is the GitHub event type this filter applies to.
// +kubebuilder:validation:Required
Event string `json:"event"`

// Action filters by webhook action (e.g., "created", "opened", "submitted").
// +optional
Action string `json:"action,omitempty"`

// BodyContains filters by substring match on the comment/review body.
// +optional
BodyContains string `json:"bodyContains,omitempty"`

// Labels requires the issue/PR to have all of these labels.
// +optional
Labels []string `json:"labels,omitempty"`

// State filters by issue/PR state ("open", "closed").
// +optional
State string `json:"state,omitempty"`

// Branch filters push events by branch name (exact match or glob).
// +optional
Branch string `json:"branch,omitempty"`

// Draft filters PRs by draft status. nil = don't filter.
// +optional
Draft *bool `json:"draft,omitempty"`

// Author filters by the event sender's username.
// +optional
Author string `json:"author,omitempty"`
}

// LinearWebhook configures webhook-driven task spawning from Linear events.
type LinearWebhook struct {
// Types is the list of Linear resource types to listen for.
// e.g., "Issue", "Comment", "Project"
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinItems=1
Types []string `json:"types"`

// Filters refine which events trigger tasks (OR semantics within same type).
// If empty, all events in the Types list trigger tasks.
// +optional
Filters []LinearWebhookFilter `json:"filters,omitempty"`
}

// LinearWebhookFilter defines filtering criteria for Linear webhook events.
type LinearWebhookFilter struct {
// Type is the Linear resource type this filter applies to.
// +kubebuilder:validation:Required
Type string `json:"type"`

// Action filters by webhook action ("create", "update", "remove").
// +optional
Action string `json:"action,omitempty"`

// States filters by Linear workflow state names (e.g., "Todo", "In Progress").
// +optional
States []string `json:"states,omitempty"`

// Labels requires the issue to have all of these labels.
// +optional
Labels []string `json:"labels,omitempty"`

// ExcludeLabels excludes issues with any of these labels.
// +optional
ExcludeLabels []string `json:"excludeLabels,omitempty"`
}

// TaskTemplateMetadata holds optional labels and annotations for spawned Tasks.
type TaskTemplateMetadata struct {
// Labels are merged into the spawned Task's labels. Values support Go
Expand Down Expand Up @@ -355,6 +450,8 @@ type TaskTemplate struct {
// Available variables (all sources): {{.ID}}, {{.Title}}, {{.Kind}}
// GitHub issue/Jira sources: {{.Number}}, {{.Body}}, {{.URL}}, {{.Labels}}, {{.Comments}}
// GitHub pull request sources additionally expose: {{.Branch}}, {{.ReviewState}}, {{.ReviewComments}}
// GitHub webhook sources: {{.Event}}, {{.Action}}, {{.Sender}}, {{.Ref}}, {{.Payload}} (full payload access)
// Linear webhook sources: {{.Type}}, {{.Action}}, {{.Payload}} (full payload access)
// Cron sources: {{.Time}}, {{.Schedule}}
// +optional
Branch string `json:"branch,omitempty"`
Expand All @@ -363,6 +460,8 @@ type TaskTemplate struct {
// Available variables (all sources): {{.ID}}, {{.Title}}, {{.Kind}}
// GitHub issue/Jira sources: {{.Number}}, {{.Body}}, {{.URL}}, {{.Labels}}, {{.Comments}}
// GitHub pull request sources additionally expose: {{.Branch}}, {{.ReviewState}}, {{.ReviewComments}}
// GitHub webhook sources: {{.Event}}, {{.Action}}, {{.Sender}}, {{.Ref}}, {{.Payload}} (full payload access)
// Linear webhook sources: {{.Type}}, {{.Action}}, {{.Payload}} (full payload access)
// Cron sources: {{.Time}}, {{.Schedule}}
// +optional
PromptTemplate string `json:"promptTemplate,omitempty"`
Expand Down Expand Up @@ -396,7 +495,7 @@ type TaskTemplate struct {
}

// TaskSpawnerSpec defines the desired state of TaskSpawner.
// +kubebuilder:validation:XValidation:rule="!(has(self.when.githubIssues) || has(self.when.githubPullRequests)) || has(self.taskTemplate.workspaceRef)",message="taskTemplate.workspaceRef is required when using githubIssues or githubPullRequests source"
// +kubebuilder:validation:XValidation:rule="!(has(self.when.githubIssues) || has(self.when.githubPullRequests) || has(self.when.githubWebhook)) || has(self.taskTemplate.workspaceRef)",message="taskTemplate.workspaceRef is required when using githubIssues, githubPullRequests, or githubWebhook source"
type TaskSpawnerSpec struct {
// When defines the conditions that trigger task spawning.
// +kubebuilder:validation:Required
Expand Down
119 changes: 119 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions cmd/kelos-webhook-server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Build stage
FROM golang:1.25-alpine AS builder

WORKDIR /workspace

# Copy go mod files
COPY go.mod go.sum ./
RUN go mod download

# Copy source code
COPY . .

# Build the binary
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o webhook-server ./cmd/kelos-webhook-server

# Runtime stage
FROM gcr.io/distroless/static:nonroot

WORKDIR /

# Copy the binary
COPY --from=builder /workspace/webhook-server .

USER 65532:65532

ENTRYPOINT ["/webhook-server"]
Loading
Loading