Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,18 @@
"Bash(gh label create:*)",
"Bash(gh issue create:*)",
"Bash(gh issue create:*)",
"Bash(mkdir:*)"
"Bash(mkdir:*)",
"Bash(gh run view:*)",
"Bash(ls:*)",
"Bash(gh workflow:*)",
"Bash(gh pr merge:*)",
"Bash(git fetch:*)",
"Bash(git checkout:*)",
"Bash(cat:*)",
"Bash(for pr in 271 274 275)",
"Bash(do echo \"Merging PR #$pr\")",
"Bash(done)",
"Bash(git add:*)"
],
"deny": []
}
Expand Down
129 changes: 0 additions & 129 deletions .github/pr-webhook-utils.cjs

This file was deleted.

226 changes: 0 additions & 226 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,229 +69,3 @@ jobs:
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

pr-review:
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'pull_request'

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Send PR data to webhook for code review
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
console.log('Processing PR #' + context.issue.number + ' in ' + context.repo.owner + '/' + context.repo.repo);

try {
// Get PR details
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});

// Get PR files
const files = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
console.log('Files changed:', files.data.length);

// Setup webhook URL
const webhookUrl = '${{ vars.WEBHOOK_URL }}';

// Validate webhook URL
if (!webhookUrl || !webhookUrl.trim()) {
throw new Error('WEBHOOK_URL is not configured');
}

const url = new URL(webhookUrl);
// Ensure HTTPS is used for security
if (url.protocol !== 'https:') {
throw new Error('WEBHOOK_URL must use HTTPS protocol for security');
}

// Get PR comments
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});

// Get PR review comments
const reviewComments = await github.rest.pulls.listReviewComments({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});

// Import PR webhook utilities
const fs = require('fs');
const path = require('path');

// Define the path to the utils file
const utilsPath = path.join(process.env.GITHUB_WORKSPACE, '.github', 'pr-webhook-utils.cjs');
console.log(`Loading PR webhook utilities from: ${utilsPath}`);

// Load the utilities from the external file
const prDataUtils = require(utilsPath);

// Build PR data payload
const prData = {
id: pr.data.id,
number: pr.data.number,
title: prDataUtils.sanitizeText(pr.data.title),
body: prDataUtils.sanitizeText(pr.data.body),
state: pr.data.state,
created_at: pr.data.created_at,
updated_at: pr.data.updated_at,
repository: {
name: context.repo.repo,
owner: context.repo.owner
},
head: {
ref: pr.data.head.ref,
sha: pr.data.head.sha
},
base: {
ref: pr.data.base.ref,
sha: pr.data.base.sha
},
user: {
login: pr.data.user.login,
id: pr.data.user.id
},
// Filter sensitive files and limit payload size
changed_files: files.data
.filter(file => prDataUtils.shouldIncludeFile(file.filename))
.slice(0, 100) // Limit to 100 files max
.map(file => ({
filename: file.filename,
status: file.status,
additions: file.additions,
deletions: file.deletions,
changes: file.changes,
patch: prDataUtils.limitPatch(file.patch)
})),
// Sanitize comments
comments: comments.data
.slice(0, 100) // Limit to 100 comments max
.map(comment => ({
id: comment.id,
body: prDataUtils.sanitizeText(comment.body),
user: comment.user.login,
created_at: comment.created_at
})),
// Sanitize review comments
review_comments: reviewComments.data
.slice(0, 100) // Limit to 100 review comments max
.map(comment => ({
id: comment.id,
body: prDataUtils.sanitizeText(comment.body),
user: comment.user.login,
path: comment.path,
position: comment.position,
created_at: comment.created_at
}))
};

console.log('Sending PR data to webhook...');

// Calculate payload size for logging
const payloadSize = JSON.stringify(prData).length;
console.log(`Payload size: ${(payloadSize / 1024).toFixed(2)} KB`);

// Fail if payload is too large (>5MB)
const maxPayloadSize = 5 * 1024 * 1024;
if (payloadSize > maxPayloadSize) {
throw new Error(`Payload size (${payloadSize} bytes) exceeds maximum allowed size (${maxPayloadSize} bytes)`);
}

// Use https request
const https = require('https');

// Properly stringify and send the data using safe stringify utility
const stringifyResult = prDataUtils.safeStringify(prData);

if (!stringifyResult.success) {
console.error(`JSON stringify error: ${stringifyResult.error}`);

// Use the simplified data creator utility
const simplifiedData = prDataUtils.createSimplifiedPrData(pr, context);

// Try to stringify the simplified data
const simplifiedResult = prDataUtils.safeStringify(simplifiedData);

if (!simplifiedResult.success) {
// Last resort - send minimal JSON
console.error(`Even simplified data failed: ${simplifiedResult.error}`);
stringifyResult.data = JSON.stringify({ error: "Failed to process PR data", pr_number: context.issue.number });
} else {
console.log('Using simplified PR data instead');
stringifyResult.data = simplifiedResult.data;
}
} else {
console.log('JSON data prepared successfully');
}

// Log payload size instead of full content for security
console.log(`Payload prepared successfully: ${(stringifyResult.data.length / 1024).toFixed(2)} KB`);

const options = {
hostname: url.hostname,
port: url.port || 443,
path: url.pathname,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(stringifyResult.data),
'CF-Access-Client-Id': '${{ secrets.CF_ACCESS_CLIENT_ID }}',
'CF-Access-Client-Secret': '${{ secrets.CF_ACCESS_CLIENT_SECRET }}'
},
timeout: 10000 // 10 second timeout
};

// Make the request
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });

res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
console.log(`Successfully sent PR data to webhook (Status: ${res.statusCode})`);
} else {
const errorMsg = `Failed to send PR data to webhook: Status ${res.statusCode}`;
console.error(errorMsg);
console.error(`Response: ${data}`);
// Fail the job if the webhook returns an error
core.setFailed(errorMsg);
}
});
});

req.on('error', (error) => {
const errorMsg = `Network error when sending to webhook: ${error.message}`;
console.error(errorMsg);
core.setFailed(errorMsg);
});

req.on('timeout', () => {
req.destroy();
const errorMsg = 'Request to webhook timed out after 10 seconds';
console.error(errorMsg);
core.setFailed(errorMsg);
});

req.write(stringifyResult.data);
req.end();

} catch (error) {
console.error(`Failed to process PR data: ${error.message}`);
core.setFailed(`PR review webhook error: ${error.message}`);
}
Loading
Loading