or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-creation.mdcontext-events.mdcore-framework.mdgithub-api.mdindex.mdserver-middleware.md

context-events.mddocs/

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

```