Skip to content

Commit a061421

Browse files
committed
refactor: Implement structured logging with pino and add unhandled rejection handler
1 parent 51589fa commit a061421

2 files changed

Lines changed: 46 additions & 37 deletions

File tree

index.ts

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,55 @@
11
import express, { Application } from 'express';
22
import dotenv from 'dotenv';
3-
import { Probot, Application as ProbotApplication } from 'probot';
3+
import { Probot } from 'probot';
44
import { createNodeMiddleware } from '@octokit/webhooks';
5-
import fs from 'fs'; // Needed for reading private key if not a string
5+
import fs from 'fs';
6+
import pino from 'pino';
7+
8+
// Setup logger
9+
const logger = pino({
10+
level: process.env.LOG_LEVEL || 'info',
11+
transport: {
12+
target: 'pino-pretty',
13+
options: {
14+
colorize: true,
15+
translateTime: 'SYS:yyyy-dd-mm, h:MM:ss',
16+
ignore: 'pid,hostname',
17+
}
18+
}
19+
});
620

721
dotenv.config();
822

923
// Ensure environment variables are loaded and set
1024
const appId = process.env.APP_ID;
11-
const privateKeyPath = process.env.PRIVATE_KEY_PATH; // Suggest using a path for the private key
25+
const privateKeyPath = process.env.PRIVATE_KEY_PATH;
1226
const webhookSecret = process.env.WEBHOOK_SECRET;
13-
const appName = process.env.APP_NAME || 'om-bot'; // Use app name for mention
27+
const appName = process.env.APP_NAME || 'om-bot';
1428

1529
if (!appId || !privateKeyPath || !webhookSecret) {
16-
console.error('Missing required environment variables: APP_ID, PRIVATE_KEY_PATH, WEBHOOK_SECRET');
30+
logger.error('Missing required environment variables: APP_ID, PRIVATE_KEY_PATH, WEBHOOK_SECRET');
1731
process.exit(1);
1832
}
1933

2034
// Read the private key from the file
2135
let privateKey: string;
2236
try {
2337
privateKey = fs.readFileSync(privateKeyPath, 'utf8');
24-
} catch (error) {
25-
console.error(`Error reading private key file at ${privateKeyPath}:`, error);
38+
} catch (error: any) {
39+
logger.error(`Error reading private key file at ${privateKeyPath}: ${error.message}`);
2640
process.exit(1);
2741
}
2842

2943
const app: Application = express();
3044
app.use(express.json());
3145

3246
// Initialize Probot
33-
// Probot uses its own Octokit instance, no need to pass it.
34-
// The webhook secret is primarily used by the middleware for signature verification.
3547
const probot = new Probot({
36-
appId: Number(appId), // APP_ID should be a number
48+
appId: Number(appId),
3749
privateKey: privateKey,
3850
});
3951

4052
// Register the webhook middleware from @octokit/webhooks
41-
// This middleware verifies the webhook signature and passes the event to Probot.
4253
const webhookMiddleware = createNodeMiddleware(probot.receive, {
4354
secret: webhookSecret,
4455
});
@@ -47,6 +58,7 @@ app.use('/webhook', webhookMiddleware);
4758

4859
// Basic root route
4960
app.get('/', (req, res) => {
61+
logger.info('GET / request received');
5062
res.send('Oh My Opencode GitHub Agent is running!');
5163
});
5264

@@ -59,27 +71,20 @@ probot.on('issue_comment.created', async (context) => {
5971
const commentBody = comment.body;
6072
const commentAuthor = comment.user.login;
6173

62-
console.log(`Received issue_comment from ${commentAuthor} in ${owner}/${repo}:`, commentBody);
74+
logger.info(`Received issue_comment from ${commentAuthor} in ${owner}/${repo}: ${commentBody}`);
6375

64-
// Check for the mention and command syntax: "@om-bot create-pr <source-branch> <target-branch> \"<PR title>\""
6576
const mentionRegex = new RegExp(`@${appName} create-pr`, 'i');
6677

6778
if (mentionRegex.test(commentBody)) {
6879
const match = commentBody.match(mentionRegex);
6980
if (match && match.index !== undefined) {
7081
const commandPart = commentBody.substring(match.index + match[0].length).trim();
71-
// Regex to capture branches and a title, allowing for quotes around the title.
72-
// It tries to capture:
73-
// Group 1: source branch
74-
// Group 2: target branch
75-
// Group 3: The entire quoted title (if present) OR the rest of the string if no quotes.
76-
const commandRegex = /(\S+)\s+(\S+)(?:\s+\"([^\"]+)\")?/; // Non-greedy match for branches, then optionally a quoted title
82+
const commandRegex = /(\S+)\s+(\S+)(?:\s+\"([^\"]+)\")?/;
7783
const parts = commandPart.match(commandRegex);
7884

7985
if (parts && parts.length >= 3) {
8086
const sourceBranch = parts[1];
8187
const targetBranch = parts[2];
82-
// If a quoted title was found (parts[3]), use it. Otherwise, use the rest of the command part as title.
8388
const prTitle = parts[3] || commandPart.substring(parts[0].indexOf(parts[2]) + parts[2].length).trim();
8489

8590
if (!sourceBranch || !targetBranch || !prTitle) {
@@ -92,13 +97,11 @@ probot.on('issue_comment.created', async (context) => {
9297
return;
9398
}
9499

95-
console.log(`Attempting to create PR: source=${sourceBranch}, target=${targetBranch}, title=${prTitle}`);
100+
logger.info(`Attempting to create PR: source=${sourceBranch}, target=${targetBranch}, title=${prTitle}`);
96101

97102
try {
98-
// Check if source branch exists (optional but good practice)
99103
await context.octokit.rest.repos.getBranch({ owner, repo, branch: sourceBranch });
100104

101-
// Create the pull request
102105
const prResponse = await context.octokit.rest.pulls.create({
103106
owner,
104107
repo,
@@ -115,11 +118,11 @@ probot.on('issue_comment.created', async (context) => {
115118
body: `Successfully created pull request: ${prResponse.data.html_url}`,
116119
});
117120
} catch (error: any) {
118-
console.error('Error creating pull request:', error);
121+
logger.error({ error: error, message: 'Error creating pull request' });
119122
let errorMessage = `Failed to create pull request. Error: ${error.message}.`;
120-
if (error.status === 422) { // Unprocessable Entity - often means branches don't exist or no diff
123+
if (error.status === 422) {
121124
errorMessage += ' This could be due to non-existent branches, no differences between branches, or insufficient permissions.';
122-
} else if (error.status === 404) { // Not Found
125+
} else if (error.status === 404) {
123126
errorMessage += ' One or more branches were not found.';
124127
}
125128
await context.octokit.rest.issues.createComment({
@@ -141,24 +144,28 @@ probot.on('issue_comment.created', async (context) => {
141144
}
142145
});
143146

144-
// Handle installation event to allow users to install the app
147+
// Handle installation event
145148
probot.on('installation.created', async (context) => {
146-
console.log('GitHub App installed or updated.');
147-
// You might want to send a message to the repo's main branch README or a default issue.
148-
// For now, just logging is sufficient for the mechanism.
149+
logger.info('GitHub App installed or updated.');
150+
});
151+
152+
// Handle unhandled promise rejections
153+
process.on('unhandledRejection', (reason, promise) => {
154+
logger.error({ err: reason, promise: promise }, 'Unhandled Rejection at:');
155+
// Consider more graceful shutdown or error reporting here if necessary
149156
});
150157

151158
// Listen on a specific port
152-
const port = parseInt(process.env.PORT || '3000', 10); // Ensure port is a number
159+
const port = parseInt(process.env.PORT || '3000', 10);
153160

154161
// Start the server
155162
app.listen(port, () => {
156-
console.log(`Server listening on port ${port}`);
157-
console.log(`Webhook endpoint: /webhook`);
158-
console.log(`Make sure your GitHub App webhook is configured to point to this URL and port.`);
163+
logger.info(`Server listening on port ${port}`);
164+
logger.info(`Webhook endpoint: /webhook`);
165+
logger.info(`Make sure your GitHub App webhook is configured to point to this URL and port.`);
159166
});
160167

161168
// Optional: Log any unhandled events for debugging
162169
// probot.onAny(async (context) => {
163-
// console.log(`Unhandled event: ${context.name}`);
164-
// });
170+
// logger.debug(`Unhandled event: ${context.name}`);
171+
// });

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
"@types/express": "^5.0.6",
2323
"@types/node": "^25.0.3",
2424
"ts-node": "^10.9.2",
25-
"typescript": "^5.9.3"
25+
"typescript": "^5.9.3",
26+
"pino": "^9.0.0",
27+
"pino-pretty": "^8.0.0"
2628
}
2729
}

0 commit comments

Comments
 (0)