0
# Context and Event Handling
1
2
The Context class provides rich context objects for GitHub webhook events, including event payloads, authenticated GitHub API clients, and helper methods for common repository operations.
3
4
## Capabilities
5
6
### Context Class
7
8
The main context object passed to event handlers containing event data and GitHub API access.
9
10
```typescript { .api }
11
/**
12
* Context object for GitHub webhook events
13
* @template Event - Specific webhook event type for type safety
14
*/
15
class Context<Event extends WebhookEvents = WebhookEvents> {
16
/** GitHub webhook event name (e.g., "issues.opened") */
17
public name: WebhookEvents;
18
19
/** Unique event delivery ID from GitHub */
20
public id: string;
21
22
/** GitHub webhook event payload */
23
public payload: WebhookPayload;
24
25
/** Authenticated Octokit instance for GitHub API calls */
26
public octokit: ProbotOctokit;
27
28
/** Scoped logger instance for this event */
29
public log: Logger;
30
31
/**
32
* Create a new context instance
33
* @param event - GitHub webhook event
34
* @param octokit - Authenticated Octokit instance
35
* @param log - Logger instance
36
*/
37
constructor(event: WebhookEvent<Event>, octokit: ProbotOctokit, log: Logger);
38
}
39
```
40
41
**Usage Examples:**
42
43
```typescript
44
app.on("issues.opened", async (context) => {
45
// Access event properties
46
console.log(`Event: ${context.name}`);
47
console.log(`Event ID: ${context.id}`);
48
console.log(`Issue: ${context.payload.issue.title}`);
49
50
// Use authenticated GitHub API
51
const user = await context.octokit.users.getByUsername({
52
username: context.payload.issue.user.login,
53
});
54
55
// Log with context
56
context.log.info("Processing issue", {
57
issue: context.payload.issue.number
58
});
59
});
60
```
61
62
### Context Properties
63
64
Key properties available on every context instance.
65
66
```typescript { .api }
67
/**
68
* True if the event was triggered by a bot account
69
* Useful for avoiding infinite loops in bot interactions
70
*/
71
get isBot(): boolean;
72
```
73
74
**Usage Examples:**
75
76
```typescript
77
app.on("issues.opened", async (context) => {
78
// Skip processing if event was triggered by a bot
79
if (context.isBot) {
80
context.log.info("Skipping bot event");
81
return;
82
}
83
84
// Process human-triggered events
85
await processIssue(context);
86
});
87
```
88
89
### Repository Helpers
90
91
Helper methods for extracting common repository parameters from event payloads.
92
93
```typescript { .api }
94
/**
95
* Extract repository owner and name from event payload
96
* @param object - Optional object to merge with repo params
97
* @returns Object containing owner, repo, and any additional properties
98
* @throws Error if repository is not available in the event payload
99
*/
100
repo<T>(object?: T): RepoResultType<Event> & T;
101
102
/**
103
* Extract issue parameters from event payload
104
* @param object - Optional object to merge with issue params
105
* @returns Object containing owner, repo, issue_number, and any additional properties
106
* @throws Error if issue is not available in the event payload
107
*/
108
issue<T>(object?: T): RepoResultType<Event> & { issue_number: number } & T;
109
110
/**
111
* Extract pull request parameters from event payload
112
* @param object - Optional object to merge with PR params
113
* @returns Object containing owner, repo, pull_number, and any additional properties
114
* @throws Error if pull request is not available in the event payload
115
*/
116
pullRequest<T>(object?: T): RepoResultType<Event> & { pull_number: number } & T;
117
```
118
119
**Usage Examples:**
120
121
```typescript
122
app.on("issues.opened", async (context) => {
123
// Get repository parameters
124
const repoParams = context.repo();
125
// { owner: "octocat", repo: "Hello-World" }
126
127
// Get issue parameters with additional data
128
const issueComment = context.issue({
129
body: "Thanks for opening this issue!",
130
});
131
// { owner: "octocat", repo: "Hello-World", issue_number: 1, body: "Thanks..." }
132
133
// Create comment using helper
134
await context.octokit.issues.createComment(issueComment);
135
});
136
137
app.on("pull_request.opened", async (context) => {
138
// Get pull request parameters
139
const prParams = context.pullRequest({
140
body: "Thanks for the contribution!",
141
});
142
// { owner: "octocat", repo: "Hello-World", pull_number: 1, body: "Thanks..." }
143
144
// Create PR comment
145
await context.octokit.issues.createComment(prParams);
146
});
147
148
app.on("push", async (context) => {
149
// Get repository info for push events
150
const repoInfo = context.repo({
151
ref: "refs/heads/main",
152
});
153
154
// Get branch information
155
const branch = await context.octokit.repos.getBranch(repoInfo);
156
});
157
```
158
159
### Configuration Management
160
161
Method for reading configuration files from the repository's `.github` directory.
162
163
```typescript { .api }
164
/**
165
* Read and parse configuration file from .github directory
166
* @param fileName - Name of config file (without .yml/.yaml extension)
167
* @param defaultConfig - Default configuration to use if file doesn't exist
168
* @param deepMergeOptions - Options for deep merging default and file config
169
* @returns Parsed configuration object or null if file doesn't exist and no default
170
*/
171
async config<T>(
172
fileName: string,
173
defaultConfig?: T,
174
deepMergeOptions?: MergeOptions
175
): Promise<T | null>;
176
```
177
178
**Usage Examples:**
179
180
```typescript
181
// Define configuration type
182
interface BotConfig {
183
welcomeMessage: string;
184
autoAssign: string[];
185
labels: {
186
bug: string;
187
enhancement: string;
188
};
189
}
190
191
app.on("issues.opened", async (context) => {
192
// Read config with defaults
193
const config = await context.config<BotConfig>("bot", {
194
welcomeMessage: "Welcome to the project!",
195
autoAssign: [],
196
labels: {
197
bug: "bug",
198
enhancement: "enhancement",
199
},
200
});
201
202
if (config) {
203
// Use configuration
204
await context.octokit.issues.createComment(
205
context.issue({ body: config.welcomeMessage })
206
);
207
208
// Auto-assign if configured
209
if (config.autoAssign.length > 0) {
210
await context.octokit.issues.addAssignees(
211
context.issue({ assignees: config.autoAssign })
212
);
213
}
214
}
215
});
216
217
// Configuration file at .github/bot.yml:
218
// welcomeMessage: "Thanks for opening an issue!"
219
// autoAssign:
220
// - maintainer1
221
// - maintainer2
222
// labels:
223
// bug: "🐛 bug"
224
// enhancement: "✨ enhancement"
225
```
226
227
## Event Types and Payloads
228
229
### Common Event Names
230
231
```typescript { .api }
232
// Issue events
233
"issues.opened" | "issues.closed" | "issues.edited" | "issues.assigned" |
234
"issues.unassigned" | "issues.labeled" | "issues.unlabeled" |
235
"issues.milestoned" | "issues.demilestoned" | "issues.reopened" | "issues.transferred"
236
237
// Pull request events
238
"pull_request.opened" | "pull_request.closed" | "pull_request.edited" |
239
"pull_request.assigned" | "pull_request.unassigned" | "pull_request.review_requested" |
240
"pull_request.review_request_removed" | "pull_request.labeled" | "pull_request.unlabeled" |
241
"pull_request.synchronize" | "pull_request.reopened" | "pull_request.ready_for_review" |
242
"pull_request.converted_to_draft"
243
244
// Repository events
245
"push" | "repository.created" | "repository.deleted" | "repository.archived" |
246
"repository.unarchived" | "repository.publicized" | "repository.privatized"
247
248
// Installation events
249
"installation.created" | "installation.deleted" | "installation_repositories.added" |
250
"installation_repositories.removed"
251
252
// Other common events
253
"check_run" | "check_suite" | "commit_comment" | "create" | "delete" | "deployment" |
254
"deployment_status" | "fork" | "gollum" | "issue_comment" | "member" | "milestone" |
255
"page_build" | "project" | "project_card" | "project_column" | "public" | "pull_request_review" |
256
"pull_request_review_comment" | "release" | "status" | "team" | "watch"
257
```
258
259
### Payload Structure Examples
260
261
```typescript { .api }
262
// Issue event payload structure
263
interface IssuePayload {
264
action: "opened" | "closed" | "edited" | "assigned" | "unassigned" | "labeled" | "unlabeled";
265
issue: {
266
id: number;
267
number: number;
268
title: string;
269
body: string;
270
user: User;
271
state: "open" | "closed";
272
labels: Label[];
273
assignees: User[];
274
milestone: Milestone | null;
275
created_at: string;
276
updated_at: string;
277
};
278
repository: Repository;
279
sender: User;
280
}
281
282
// Pull request event payload structure
283
interface PullRequestPayload {
284
action: "opened" | "closed" | "edited" | "assigned" | "unassigned" | "synchronize";
285
number: number;
286
pull_request: {
287
id: number;
288
number: number;
289
title: string;
290
body: string;
291
user: User;
292
state: "open" | "closed";
293
merged: boolean;
294
head: {
295
ref: string;
296
sha: string;
297
repo: Repository;
298
};
299
base: {
300
ref: string;
301
sha: string;
302
repo: Repository;
303
};
304
created_at: string;
305
updated_at: string;
306
};
307
repository: Repository;
308
sender: User;
309
}
310
311
// Push event payload structure
312
interface PushPayload {
313
ref: string;
314
before: string;
315
after: string;
316
commits: Commit[];
317
head_commit: Commit | null;
318
repository: Repository;
319
pusher: User;
320
sender: User;
321
}
322
```
323
324
## Helper Types
325
326
```typescript { .api }
327
type WebhookEvents = string; // Actual webhook event names from @octokit/webhooks
328
type WebhookPayload = any; // Actual payload types from @octokit/webhooks-types
329
330
type RepoResultType<E extends WebhookEvents> = {
331
owner: string;
332
repo: string;
333
};
334
335
type MergeOptions = {
336
/** How arrays should be merged */
337
arrayMerge?: (target: any[], source: any[], options: MergeOptions) => any[];
338
/** Whether to clone values */
339
clone?: boolean;
340
/** Custom merge function */
341
customMerge?: (key: string, options: MergeOptions) => ((target: any, source: any) => any) | undefined;
342
};
343
344
interface User {
345
id: number;
346
login: string;
347
avatar_url: string;
348
type: "User" | "Bot";
349
}
350
351
interface Repository {
352
id: number;
353
name: string;
354
full_name: string;
355
owner: User;
356
private: boolean;
357
description: string | null;
358
default_branch: string;
359
}
360
361
interface Label {
362
id: number;
363
name: string;
364
color: string;
365
description: string | null;
366
}
367
368
interface Milestone {
369
id: number;
370
number: number;
371
title: string;
372
description: string | null;
373
state: "open" | "closed";
374
due_on: string | null;
375
}
376
377
interface Commit {
378
id: string;
379
message: string;
380
author: {
381
name: string;
382
email: string;
383
username?: string;
384
};
385
url: string;
386
distinct: boolean;
387
added: string[];
388
removed: string[];
389
modified: string[];
390
}
391
```