A BubuStack Impulse that submits durable StoryTrigger requests from GitHub webhook events.
- HMAC-SHA256 signature validation - Secure webhook verification
- Event filtering - Filter by event type, action, repository, branch
- Smart session keys - Automatic session keying by PR/issue/commit
- Policy-based routing - Route different events to different Stories
- End event detection - Graceful session cleanup on PR close/merge
- Install the ImpulseTemplate:
kubectl apply -f Impulse.yaml- Create an Impulse instance:
apiVersion: bubustack.io/v1alpha1kind: Impulsemetadata:
name: my-github-webhooknamespace: defaultspec:
templateRef:
name: github-webhook-impulsestoryRef:
name: my-github-storysecrets:
webhookSecret:
name: github-webhook-secretwith:
path: /webhookvalidateSignature: trueeventsAllowlist:
- pull_request
- issuesactionsAllowlist:
- opened
- closed
- labeled- Create the webhook secret:
kubectl create secret generic github-webhook-secret \
--from-literal=WEBHOOK_SECRET=your-github-webhook-secret- Configure the webhook in GitHub:
- Go to your repository → Settings → Webhooks → Add webhook
- Payload URL:
https://your-ingress/webhook - Content type:
application/json - Secret: Same as
WEBHOOK_SECRET - Events: Select events you want to receive
with:
# Only accept these event typeseventsAllowlist:
- pull_request
- push
- issues# Only accept these actionsactionsAllowlist:
- opened
- synchronize
- closed# Only accept from these repositoriesrepositoriesAllowlist:
- owner/repo1
- owner/repo2# Only accept these branches (supports globs)branchesAllowlist:
- main
- release/*with:
# auto: Smart detection based on event type (default)# - PR events → repo-pr-123# - Issue events → repo-issue-456# - Push events → repo-refs/heads/mainsessionKeyStrategy: auto# delivery: Use unique X-GitHub-Delivery headersessionKeyStrategy: delivery# custom: evaluate a template expression# available variables: .event, .action, .deliveryId, .payload, .repositorysessionKeyStrategy: customsessionKeyExpression: 'printf "%s-pr-%v" .repository.full_name .payload.pull_request.number'Route different events to different Stories:
with:
policies:
- name: pr-reviewevents:
- pull_requestactions:
- opened
- synchronizestoryName: pr-review-story
- name: issue-triageevents:
- issuesactions:
- openedstoryName: issue-triage-story
- name: release-notifyevents:
- releaseactions:
- publishedstoryName: release-notification-storywith:
# Events that start a new sessionstartEvents:
- "pull_request:opened"# Events that end a session (calls StoryDispatcher.Stop)endEvents:
- "pull_request:closed"
- "pull_request:merged"The impulse provides structured inputs to your Story:
# Common fields (all events)event: "pull_request"action: "opened"deliveryId: "abc123..."repository: "owner/repo"sender: "username"# PR-specific fieldspullRequest:
number: 123title: "Add feature X"state: "open"htmlUrl: "https://github.com/..."head:
ref: "feature-branch"sha: "abc123..."base:
ref: "main"sha: "def456..."user: "author"draft: falsemerged: false# Issue-specific fieldsissue:
number: 456title: "Bug report"state: "open"labels: ["bug", "priority-high"]# Comment fields (issue_comment)comment:
id: 789body: "This is a comment"user: "commenter"# Push-specific fieldspush:
ref: "refs/heads/main"before: "abc123..."after: "def456..."commits: [...]pusher: "username"apiVersion: bubustack.io/v1alpha1kind: Storymetadata:
name: pr-review-assistantspec:
pattern: batchsteps:
- name: fetch-diffref:
name: http-requestwith:
url: "https://api.github.com/repos/{{ inputs.repository }}/pulls/{{ inputs.pullRequest.number }}"headers:
Accept: "application/vnd.github.v3.diff"
- name: reviewneeds: [fetch-diff]ref:
name: ai-reviewerwith:
code: "{{ steps['fetch-diff'].output.body }}"
- name: post-commentneeds: [review]ref:
name: github-mcpwith:
action: callTooltool: create_issue_commentarguments:
owner: "{{ inputs.repository | split('/') | first }}"repo: "{{ inputs.repository | split('/') | last }}"issue_number: "{{ inputs.pullRequest.number }}"body: "{{ steps['review'].output.text }}"apiVersion: bubustack.io/v1alpha1kind: Storymetadata:
name: issue-triage-botspec:
pattern: batchsteps:
- name: analyzeref:
name: openai-chatwith:
userPrompt: | Analyze this GitHub issue and suggest: 1. Priority (P0-P3) 2. Labels (bug, feature, docs, etc.) 3. Suggested assignee team Title: {{ inputs.issue.title }} Body: {{ inputs.issue.body }} - name: apply-labelsneeds: [analyze]ref:
name: github-mcpwith:
action: callTooltool: add_issue_labelsarguments:
owner: "{{ inputs.repository | split('/') | first }}"repo: "{{ inputs.repository | split('/') | last }}"issue_number: "{{ inputs.issue.number }}"labels: "{{ steps['analyze'].output.structured.labels }}"GET :8081/health- Liveness probeGET :8081/ready- Readiness probe
# Build binary
make build
# Run tests
make test# Build Docker image
make docker-build VERSION=v0.1.0
# Push to registry
make docker-push VERSION=v0.1.0Copyright 2025 BubuStack.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.