A framework for building GitHub Apps to automate and improve your workflow
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
The main context object passed to event handlers containing event data and GitHub API access.
/**
* Context object for GitHub webhook events
* @template Event - Specific webhook event type for type safety
*/
class Context<Event extends WebhookEvents = WebhookEvents> {
/** GitHub webhook event name (e.g., "issues.opened") */
public name: WebhookEvents;
/** Unique event delivery ID from GitHub */
public id: string;
/** GitHub webhook event payload */
public payload: WebhookPayload;
/** Authenticated Octokit instance for GitHub API calls */
public octokit: ProbotOctokit;
/** Scoped logger instance for this event */
public log: Logger;
/**
* Create a new context instance
* @param event - GitHub webhook event
* @param octokit - Authenticated Octokit instance
* @param log - Logger instance
*/
constructor(event: WebhookEvent<Event>, octokit: ProbotOctokit, log: Logger);
}Usage Examples:
app.on("issues.opened", async (context) => {
// Access event properties
console.log(`Event: ${context.name}`);
console.log(`Event ID: ${context.id}`);
console.log(`Issue: ${context.payload.issue.title}`);
// Use authenticated GitHub API
const user = await context.octokit.users.getByUsername({
username: context.payload.issue.user.login,
});
// Log with context
context.log.info("Processing issue", {
issue: context.payload.issue.number
});
});Key properties available on every context instance.
/**
* True if the event was triggered by a bot account
* Useful for avoiding infinite loops in bot interactions
*/
get isBot(): boolean;Usage Examples:
app.on("issues.opened", async (context) => {
// Skip processing if event was triggered by a bot
if (context.isBot) {
context.log.info("Skipping bot event");
return;
}
// Process human-triggered events
await processIssue(context);
});Helper methods for extracting common repository parameters from event payloads.
/**
* Extract repository owner and name from event payload
* @param object - Optional object to merge with repo params
* @returns Object containing owner, repo, and any additional properties
* @throws Error if repository is not available in the event payload
*/
repo<T>(object?: T): RepoResultType<Event> & T;
/**
* Extract issue parameters from event payload
* @param object - Optional object to merge with issue params
* @returns Object containing owner, repo, issue_number, and any additional properties
* @throws Error if issue is not available in the event payload
*/
issue<T>(object?: T): RepoResultType<Event> & { issue_number: number } & T;
/**
* Extract pull request parameters from event payload
* @param object - Optional object to merge with PR params
* @returns Object containing owner, repo, pull_number, and any additional properties
* @throws Error if pull request is not available in the event payload
*/
pullRequest<T>(object?: T): RepoResultType<Event> & { pull_number: number } & T;Usage Examples:
app.on("issues.opened", async (context) => {
// Get repository parameters
const repoParams = context.repo();
// { owner: "octocat", repo: "Hello-World" }
// Get issue parameters with additional data
const issueComment = context.issue({
body: "Thanks for opening this issue!",
});
// { owner: "octocat", repo: "Hello-World", issue_number: 1, body: "Thanks..." }
// Create comment using helper
await context.octokit.issues.createComment(issueComment);
});
app.on("pull_request.opened", async (context) => {
// Get pull request parameters
const prParams = context.pullRequest({
body: "Thanks for the contribution!",
});
// { owner: "octocat", repo: "Hello-World", pull_number: 1, body: "Thanks..." }
// Create PR comment
await context.octokit.issues.createComment(prParams);
});
app.on("push", async (context) => {
// Get repository info for push events
const repoInfo = context.repo({
ref: "refs/heads/main",
});
// Get branch information
const branch = await context.octokit.repos.getBranch(repoInfo);
});Method for reading configuration files from the repository's .github directory.
/**
* Read and parse configuration file from .github directory
* @param fileName - Name of config file (without .yml/.yaml extension)
* @param defaultConfig - Default configuration to use if file doesn't exist
* @param deepMergeOptions - Options for deep merging default and file config
* @returns Parsed configuration object or null if file doesn't exist and no default
*/
async config<T>(
fileName: string,
defaultConfig?: T,
deepMergeOptions?: MergeOptions
): Promise<T | null>;Usage Examples:
// Define configuration type
interface BotConfig {
welcomeMessage: string;
autoAssign: string[];
labels: {
bug: string;
enhancement: string;
};
}
app.on("issues.opened", async (context) => {
// Read config with defaults
const config = await context.config<BotConfig>("bot", {
welcomeMessage: "Welcome to the project!",
autoAssign: [],
labels: {
bug: "bug",
enhancement: "enhancement",
},
});
if (config) {
// Use configuration
await context.octokit.issues.createComment(
context.issue({ body: config.welcomeMessage })
);
// Auto-assign if configured
if (config.autoAssign.length > 0) {
await context.octokit.issues.addAssignees(
context.issue({ assignees: config.autoAssign })
);
}
}
});
// Configuration file at .github/bot.yml:
// welcomeMessage: "Thanks for opening an issue!"
// autoAssign:
// - maintainer1
// - maintainer2
// labels:
// bug: "🐛 bug"
// enhancement: "✨ enhancement"// Issue events
"issues.opened" | "issues.closed" | "issues.edited" | "issues.assigned" |
"issues.unassigned" | "issues.labeled" | "issues.unlabeled" |
"issues.milestoned" | "issues.demilestoned" | "issues.reopened" | "issues.transferred"
// Pull request events
"pull_request.opened" | "pull_request.closed" | "pull_request.edited" |
"pull_request.assigned" | "pull_request.unassigned" | "pull_request.review_requested" |
"pull_request.review_request_removed" | "pull_request.labeled" | "pull_request.unlabeled" |
"pull_request.synchronize" | "pull_request.reopened" | "pull_request.ready_for_review" |
"pull_request.converted_to_draft"
// Repository events
"push" | "repository.created" | "repository.deleted" | "repository.archived" |
"repository.unarchived" | "repository.publicized" | "repository.privatized"
// Installation events
"installation.created" | "installation.deleted" | "installation_repositories.added" |
"installation_repositories.removed"
// Other common events
"check_run" | "check_suite" | "commit_comment" | "create" | "delete" | "deployment" |
"deployment_status" | "fork" | "gollum" | "issue_comment" | "member" | "milestone" |
"page_build" | "project" | "project_card" | "project_column" | "public" | "pull_request_review" |
"pull_request_review_comment" | "release" | "status" | "team" | "watch"// Issue event payload structure
interface IssuePayload {
action: "opened" | "closed" | "edited" | "assigned" | "unassigned" | "labeled" | "unlabeled";
issue: {
id: number;
number: number;
title: string;
body: string;
user: User;
state: "open" | "closed";
labels: Label[];
assignees: User[];
milestone: Milestone | null;
created_at: string;
updated_at: string;
};
repository: Repository;
sender: User;
}
// Pull request event payload structure
interface PullRequestPayload {
action: "opened" | "closed" | "edited" | "assigned" | "unassigned" | "synchronize";
number: number;
pull_request: {
id: number;
number: number;
title: string;
body: string;
user: User;
state: "open" | "closed";
merged: boolean;
head: {
ref: string;
sha: string;
repo: Repository;
};
base: {
ref: string;
sha: string;
repo: Repository;
};
created_at: string;
updated_at: string;
};
repository: Repository;
sender: User;
}
// Push event payload structure
interface PushPayload {
ref: string;
before: string;
after: string;
commits: Commit[];
head_commit: Commit | null;
repository: Repository;
pusher: User;
sender: User;
}type WebhookEvents = string; // Actual webhook event names from @octokit/webhooks
type WebhookPayload = any; // Actual payload types from @octokit/webhooks-types
type RepoResultType<E extends WebhookEvents> = {
owner: string;
repo: string;
};
type MergeOptions = {
/** How arrays should be merged */
arrayMerge?: (target: any[], source: any[], options: MergeOptions) => any[];
/** Whether to clone values */
clone?: boolean;
/** Custom merge function */
customMerge?: (key: string, options: MergeOptions) => ((target: any, source: any) => any) | undefined;
};
interface User {
id: number;
login: string;
avatar_url: string;
type: "User" | "Bot";
}
interface Repository {
id: number;
name: string;
full_name: string;
owner: User;
private: boolean;
description: string | null;
default_branch: string;
}
interface Label {
id: number;
name: string;
color: string;
description: string | null;
}
interface Milestone {
id: number;
number: number;
title: string;
description: string | null;
state: "open" | "closed";
due_on: string | null;
}
interface Commit {
id: string;
message: string;
author: {
name: string;
email: string;
username?: string;
};
url: string;
distinct: boolean;
added: string[];
removed: string[];
modified: string[];
}Install with Tessl CLI
npx tessl i tessl/npm-probot