mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-22 22:44:13 +08:00
feat: add comprehensive response logging for GitHub API errors
- Log entire raw response body in text, hex, and base64 formats - Capture complete request context including timing and memory usage - Add detailed error response parsing with fallback handling - Include environment and operation state context - Special handling for 500 errors with GitHub request IDs - Log all response headers including rate limit info
This commit is contained in:
93
check-pr-status.ts
Normal file
93
check-pr-status.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
// Check the status of PR #105775
|
||||||
|
|
||||||
|
const GITHUB_API_URL = "https://api.github.com";
|
||||||
|
|
||||||
|
async function checkPRStatus(token: string) {
|
||||||
|
const headers = {
|
||||||
|
Accept: "application/vnd.github+json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28",
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check PR details
|
||||||
|
console.log("Checking PR #105775...\n");
|
||||||
|
const prResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/anthropics/anthropic/pulls/105775`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`PR Status: ${prResponse.status}`);
|
||||||
|
|
||||||
|
if (!prResponse.ok) {
|
||||||
|
if (prResponse.status === 404) {
|
||||||
|
console.log("PR not found - it might be in a private repo or deleted");
|
||||||
|
}
|
||||||
|
const error = await prResponse.text();
|
||||||
|
console.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prData = await prResponse.json();
|
||||||
|
console.log(`Title: ${prData.title}`);
|
||||||
|
console.log(`State: ${prData.state}`);
|
||||||
|
console.log(`Branch: ${prData.head.ref}`);
|
||||||
|
console.log(`Base: ${prData.base.ref}`);
|
||||||
|
console.log(`Created: ${prData.created_at}`);
|
||||||
|
console.log(`Updated: ${prData.updated_at}`);
|
||||||
|
|
||||||
|
// Check if branch still exists
|
||||||
|
console.log(`\nChecking if branch '${prData.head.ref}' still exists...`);
|
||||||
|
const branchResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/anthropics/anthropic/git/refs/heads/${prData.head.ref}`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (branchResponse.ok) {
|
||||||
|
const branchData = await branchResponse.json();
|
||||||
|
console.log(`✓ Branch exists with SHA: ${branchData.object.sha}`);
|
||||||
|
console.log(` PR head SHA: ${prData.head.sha}`);
|
||||||
|
if (branchData.object.sha !== prData.head.sha) {
|
||||||
|
console.log(` ⚠️ Branch has been updated since PR was created`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`✗ Branch does not exist (${branchResponse.status})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get recent comments
|
||||||
|
console.log(`\nFetching recent comments...`);
|
||||||
|
const commentsResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/anthropics/anthropic/issues/105775/comments?per_page=5&sort=created&direction=desc`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (commentsResponse.ok) {
|
||||||
|
const comments = await commentsResponse.json();
|
||||||
|
console.log(`Found ${comments.length} recent comments:`);
|
||||||
|
|
||||||
|
comments.reverse().forEach((comment: any, index: number) => {
|
||||||
|
console.log(`\nComment ${index + 1}:`);
|
||||||
|
console.log(` Author: ${comment.user.login}`);
|
||||||
|
console.log(` Created: ${comment.created_at}`);
|
||||||
|
console.log(` Body preview: ${comment.body.substring(0, 100)}...`);
|
||||||
|
|
||||||
|
// Check if it's a claude-code-action comment
|
||||||
|
if (comment.body.includes("claude") || comment.user.login.includes("bot")) {
|
||||||
|
console.log(` → Appears to be a Claude-related comment`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = process.argv[2];
|
||||||
|
if (!token) {
|
||||||
|
console.log("Usage: bun check-pr-status.ts <github-pat>");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkPRStatus(token);
|
||||||
183
diagnose-app-permissions.ts
Normal file
183
diagnose-app-permissions.ts
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
// Diagnose why GitHub App permissions are inconsistent
|
||||||
|
|
||||||
|
const GITHUB_API_URL = "https://api.github.com";
|
||||||
|
|
||||||
|
async function diagnosePermissions(token: string, owner: string, repo: string) {
|
||||||
|
const headers = {
|
||||||
|
Accept: "application/vnd.github+json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28",
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`\n=== Diagnosing GitHub App Permission Issues ===`);
|
||||||
|
console.log(`Repository: ${owner}/${repo}\n`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Check what type of token we have
|
||||||
|
console.log("1. Token Analysis:");
|
||||||
|
const authHeader = headers.Authorization;
|
||||||
|
if (authHeader.includes('ghs_')) {
|
||||||
|
console.log("✓ GitHub App installation token detected");
|
||||||
|
} else if (authHeader.includes('ghp_')) {
|
||||||
|
console.log("✓ Personal Access Token detected");
|
||||||
|
} else {
|
||||||
|
console.log("? Unknown token type");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Check rate limit headers (different for apps vs users)
|
||||||
|
console.log("\n2. Rate Limit Analysis:");
|
||||||
|
const rateLimitResponse = await fetch(`${GITHUB_API_URL}/rate_limit`, { headers });
|
||||||
|
if (rateLimitResponse.ok) {
|
||||||
|
const rateData = await rateLimitResponse.json();
|
||||||
|
console.log(` Core limit: ${rateData.rate.remaining}/${rateData.rate.limit}`);
|
||||||
|
if (rateData.rate.limit > 5000) {
|
||||||
|
console.log(" → Higher limit suggests GitHub App token");
|
||||||
|
} else {
|
||||||
|
console.log(" → Standard limit suggests user token");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Test different API endpoints to find permission boundaries
|
||||||
|
console.log("\n3. Testing API Endpoints:");
|
||||||
|
|
||||||
|
// Test regular content API
|
||||||
|
console.log("\n a) Content API (high-level):");
|
||||||
|
const contentResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/contents/README.md`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
console.log(` GET contents: ${contentResponse.status} ${contentResponse.ok ? '✓' : '✗'}`);
|
||||||
|
|
||||||
|
// Test git database read
|
||||||
|
console.log("\n b) Git Database API (read):");
|
||||||
|
const branchResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/branches`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
if (branchResponse.ok) {
|
||||||
|
const branches = await branchResponse.json();
|
||||||
|
const defaultBranch = branches.find((b: any) => b.name === 'main' || b.name === 'master' || b.name === 'staging');
|
||||||
|
if (defaultBranch) {
|
||||||
|
const commitSha = defaultBranch.commit.sha;
|
||||||
|
|
||||||
|
// Try to read commit
|
||||||
|
const commitResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/git/commits/${commitSha}`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
console.log(` GET commit: ${commitResponse.status} ${commitResponse.ok ? '✓' : '✗'}`);
|
||||||
|
|
||||||
|
// Try to read tree
|
||||||
|
if (commitResponse.ok) {
|
||||||
|
const commitData = await commitResponse.json();
|
||||||
|
const treeResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/git/trees/${commitData.tree.sha}`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
console.log(` GET tree: ${treeResponse.status} ${treeResponse.ok ? '✓' : '✗'}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test git database write
|
||||||
|
console.log("\n c) Git Database API (write):");
|
||||||
|
|
||||||
|
// Get a base commit to test with
|
||||||
|
const testBranch = branches[0];
|
||||||
|
if (testBranch) {
|
||||||
|
const baseCommitResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/git/commits/${testBranch.commit.sha}`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (baseCommitResponse.ok) {
|
||||||
|
const baseCommit = await baseCommitResponse.json();
|
||||||
|
|
||||||
|
// Try to create a blob
|
||||||
|
const blobResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/git/blobs`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
...headers,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
content: "test",
|
||||||
|
encoding: "utf-8",
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log(` POST blob: ${blobResponse.status} ${blobResponse.ok ? '✓' : '✗'}`);
|
||||||
|
|
||||||
|
// Try to create a tree
|
||||||
|
const treeResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/git/trees`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
...headers,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
base_tree: baseCommit.tree.sha,
|
||||||
|
tree: [{
|
||||||
|
path: "test-permission-check.txt",
|
||||||
|
mode: "100644",
|
||||||
|
type: "blob",
|
||||||
|
content: "test",
|
||||||
|
}],
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log(` POST tree: ${treeResponse.status} ${treeResponse.ok ? '✓' : '✗'}`);
|
||||||
|
|
||||||
|
if (!treeResponse.ok) {
|
||||||
|
const error = await treeResponse.text();
|
||||||
|
console.log(` Error: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Check webhook/app events
|
||||||
|
console.log("\n4. Checking Recent Activity:");
|
||||||
|
const eventsResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/events?per_page=10`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (eventsResponse.ok) {
|
||||||
|
const events = await eventsResponse.json();
|
||||||
|
const appEvents = events.filter((e: any) =>
|
||||||
|
e.actor.login.includes('[bot]') ||
|
||||||
|
e.actor.type === 'Bot'
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(` Found ${appEvents.length} bot/app events in last 10 events`);
|
||||||
|
appEvents.forEach((event: any) => {
|
||||||
|
console.log(` - ${event.actor.login}: ${event.type} at ${event.created_at}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Summary and recommendations
|
||||||
|
console.log("\n=== Analysis Summary ===");
|
||||||
|
console.log("\nPossible causes for inconsistent 500 errors:");
|
||||||
|
console.log("1. Race conditions with other bots (check events above)");
|
||||||
|
console.log("2. Token scope varies based on who triggered the action");
|
||||||
|
console.log("3. GitHub App needs to be reinstalled/reconfigured");
|
||||||
|
console.log("4. Branch-specific protection rules");
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("\nError during diagnosis:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [token, owner, repo] = process.argv.slice(2);
|
||||||
|
if (!token || !owner || !repo) {
|
||||||
|
console.log("Usage: bun diagnose-app-permissions.ts <token> <owner> <repo>");
|
||||||
|
console.log("\nThis script helps diagnose why GitHub App permissions are inconsistent.");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnosePermissions(token, owner, repo);
|
||||||
112
revert-test-commit.ts
Normal file
112
revert-test-commit.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
// Revert the test commit we just made
|
||||||
|
|
||||||
|
const GITHUB_API_URL = "https://api.github.com";
|
||||||
|
|
||||||
|
async function revertTestCommit(token: string) {
|
||||||
|
const owner = "anthropics";
|
||||||
|
const repo = "anthropic";
|
||||||
|
const branch = "monty/fixing-pipeline-runner";
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Accept: "application/vnd.github+json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28",
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`\n=== Reverting test commit ===`);
|
||||||
|
console.log(`Repository: ${owner}/${repo}`);
|
||||||
|
console.log(`Branch: ${branch}\n`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get current branch state
|
||||||
|
console.log("Getting current branch reference...");
|
||||||
|
const refUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs/heads/${branch}`;
|
||||||
|
const refResponse = await fetch(refUrl, { headers });
|
||||||
|
|
||||||
|
if (!refResponse.ok) {
|
||||||
|
console.error(`Failed to get branch: ${refResponse.status}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const refData = await refResponse.json();
|
||||||
|
const currentSha = refData.object.sha;
|
||||||
|
console.log(`Current branch SHA: ${currentSha}`);
|
||||||
|
|
||||||
|
// Get the current commit to find its parent
|
||||||
|
console.log("\nGetting current commit details...");
|
||||||
|
const commitResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/git/commits/${currentSha}`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!commitResponse.ok) {
|
||||||
|
console.error(`Failed to get commit: ${commitResponse.status}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const commitData = await commitResponse.json();
|
||||||
|
console.log(`Current commit message: "${commitData.message}"`);
|
||||||
|
|
||||||
|
if (!commitData.message.includes("Debug: Test commit to reproduce 500 error")) {
|
||||||
|
console.log("⚠️ Current commit doesn't look like our test commit");
|
||||||
|
console.log("Are you sure you want to revert this?");
|
||||||
|
console.log("Current message:", commitData.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commitData.parents.length === 0) {
|
||||||
|
console.error("Cannot revert: this appears to be the initial commit");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentSha = commitData.parents[0].sha;
|
||||||
|
console.log(`Parent SHA: ${parentSha}`);
|
||||||
|
|
||||||
|
// Reset the branch to the parent commit
|
||||||
|
console.log("\nReverting branch to parent commit...");
|
||||||
|
const updateRefResponse = await fetch(refUrl, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
...headers,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sha: parentSha,
|
||||||
|
force: true, // Force is needed for this kind of reset
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updateRefResponse.ok) {
|
||||||
|
console.error(`Failed to revert: ${updateRefResponse.status}`);
|
||||||
|
const error = await updateRefResponse.text();
|
||||||
|
console.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("✅ Successfully reverted test commit!");
|
||||||
|
console.log(`Branch ${branch} is now back to SHA: ${parentSha}`);
|
||||||
|
|
||||||
|
// Verify the revert
|
||||||
|
console.log("\nVerifying revert...");
|
||||||
|
const verifyResponse = await fetch(refUrl, { headers });
|
||||||
|
const verifyData = await verifyResponse.json();
|
||||||
|
|
||||||
|
if (verifyData.object.sha === parentSha) {
|
||||||
|
console.log("✅ Revert confirmed");
|
||||||
|
} else {
|
||||||
|
console.log("⚠️ Unexpected SHA after revert:", verifyData.object.sha);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = process.argv[2];
|
||||||
|
if (!token) {
|
||||||
|
console.log("Usage: bun revert-test-commit.ts <github-pat>");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
revertTestCommit(token);
|
||||||
@@ -220,70 +220,155 @@ server.tool(
|
|||||||
console.error(`[commit_files] Updating reference: ${updateRefUrl}`);
|
console.error(`[commit_files] Updating reference: ${updateRefUrl}`);
|
||||||
console.error(`[commit_files] New commit SHA: ${newCommitData.sha}`);
|
console.error(`[commit_files] New commit SHA: ${newCommitData.sha}`);
|
||||||
console.error(`[commit_files] Base SHA was: ${baseSha}`);
|
console.error(`[commit_files] Base SHA was: ${baseSha}`);
|
||||||
console.error(`[commit_files] Request body:`, JSON.stringify({
|
|
||||||
|
// Log full request context before making the request
|
||||||
|
const requestBody = JSON.stringify({
|
||||||
sha: newCommitData.sha,
|
sha: newCommitData.sha,
|
||||||
force: false,
|
force: false,
|
||||||
}));
|
});
|
||||||
|
const requestHeaders = {
|
||||||
|
Accept: "application/vnd.github+json",
|
||||||
|
Authorization: `Bearer ${githubToken}`,
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
console.error(`[commit_files] Full request details:`, {
|
||||||
|
url: updateRefUrl,
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
...requestHeaders,
|
||||||
|
Authorization: `Bearer [TOKEN_LENGTH:${githubToken?.length || 0}]`,
|
||||||
|
},
|
||||||
|
body: requestBody,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
environment: {
|
||||||
|
NODE_VERSION: process.version,
|
||||||
|
PLATFORM: process.platform,
|
||||||
|
ARCH: process.arch,
|
||||||
|
},
|
||||||
|
previousOperations: {
|
||||||
|
treeCreated: treeData?.sha ? 'YES' : 'NO',
|
||||||
|
commitCreated: newCommitData?.sha ? 'YES' : 'NO',
|
||||||
|
treeSha: treeData?.sha,
|
||||||
|
commitSha: newCommitData?.sha,
|
||||||
|
baseSha: baseSha,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Log memory usage before request
|
||||||
|
const memoryBefore = process.memoryUsage();
|
||||||
|
console.error(`[commit_files] Memory before request:`, {
|
||||||
|
rss: `${(memoryBefore.rss / 1024 / 1024).toFixed(2)} MB`,
|
||||||
|
heapUsed: `${(memoryBefore.heapUsed / 1024 / 1024).toFixed(2)} MB`,
|
||||||
|
});
|
||||||
|
|
||||||
let updateRefResponse;
|
let updateRefResponse;
|
||||||
|
const requestStartTime = Date.now();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
updateRefResponse = await fetch(updateRefUrl, {
|
updateRefResponse = await fetch(updateRefUrl, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: {
|
headers: requestHeaders,
|
||||||
Accept: "application/vnd.github+json",
|
body: requestBody,
|
||||||
Authorization: `Bearer ${githubToken}`,
|
|
||||||
"X-GitHub-Api-Version": "2022-11-28",
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
sha: newCommitData.sha,
|
|
||||||
force: false,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
} catch (fetchError) {
|
} catch (fetchError) {
|
||||||
console.error(`[commit_files] FETCH ERROR during reference update:`, fetchError);
|
const requestDuration = Date.now() - requestStartTime;
|
||||||
|
console.error(`[commit_files] FETCH ERROR during reference update after ${requestDuration}ms:`, fetchError);
|
||||||
logDetailedError('commit_files_fetch', fetchError);
|
logDetailedError('commit_files_fetch', fetchError);
|
||||||
throw new Error(`Network error during reference update: ${fetchError?.message || 'Unknown fetch error'}`);
|
throw new Error(`Network error during reference update after ${requestDuration}ms: ${fetchError?.message || 'Unknown fetch error'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const requestDuration = Date.now() - requestStartTime;
|
||||||
|
console.error(`[commit_files] Request completed in ${requestDuration}ms`);
|
||||||
|
console.error(`[commit_files] Response received at: ${new Date().toISOString()}`);
|
||||||
|
|
||||||
console.error(`[commit_files] Update reference response status: ${updateRefResponse.status}`);
|
console.error(`[commit_files] Update reference response status: ${updateRefResponse.status}`);
|
||||||
console.error(`[commit_files] Response headers:`, Object.fromEntries(updateRefResponse.headers.entries()));
|
console.error(`[commit_files] Response headers:`, Object.fromEntries(updateRefResponse.headers.entries()));
|
||||||
|
|
||||||
|
// Log specific important headers
|
||||||
|
console.error(`[commit_files] Key response headers:`, {
|
||||||
|
'x-github-request-id': updateRefResponse.headers.get('x-github-request-id'),
|
||||||
|
'x-ratelimit-remaining': updateRefResponse.headers.get('x-ratelimit-remaining'),
|
||||||
|
'x-ratelimit-reset': updateRefResponse.headers.get('x-ratelimit-reset'),
|
||||||
|
'content-type': updateRefResponse.headers.get('content-type'),
|
||||||
|
'content-length': updateRefResponse.headers.get('content-length'),
|
||||||
|
'server': updateRefResponse.headers.get('server'),
|
||||||
|
});
|
||||||
|
|
||||||
if (!updateRefResponse.ok) {
|
if (!updateRefResponse.ok) {
|
||||||
let errorText;
|
console.error(`[commit_files] ERROR RESPONSE - Status: ${updateRefResponse.status} ${updateRefResponse.statusText}`);
|
||||||
|
|
||||||
|
// Capture the entire raw response body
|
||||||
|
let responseArrayBuffer;
|
||||||
|
let responseText = '';
|
||||||
|
let responseHex = '';
|
||||||
|
let responseBase64 = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
errorText = await updateRefResponse.text();
|
// Clone the response so we can read it multiple ways
|
||||||
} catch (textError) {
|
const clonedResponse = updateRefResponse.clone();
|
||||||
console.error(`[commit_files] Failed to read error response text:`, textError);
|
|
||||||
errorText = 'Unable to read error response';
|
// Get raw bytes
|
||||||
|
responseArrayBuffer = await updateRefResponse.arrayBuffer();
|
||||||
|
const responseBytes = new Uint8Array(responseArrayBuffer);
|
||||||
|
|
||||||
|
// Convert to text (with error handling for non-UTF8)
|
||||||
|
responseText = new TextDecoder('utf-8', { fatal: false }).decode(responseBytes);
|
||||||
|
|
||||||
|
// Convert to hex for debugging binary responses
|
||||||
|
responseHex = Array.from(responseBytes.slice(0, 1000)) // First 1000 bytes to avoid huge logs
|
||||||
|
.map(b => b.toString(16).padStart(2, '0'))
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
// Convert to base64
|
||||||
|
responseBase64 = Buffer.from(responseBytes).toString('base64');
|
||||||
|
|
||||||
|
console.error(`[commit_files] COMPLETE ERROR RESPONSE:`);
|
||||||
|
console.error(`[commit_files] ===== RESPONSE BODY (TEXT) =====`);
|
||||||
|
console.error(responseText);
|
||||||
|
console.error(`[commit_files] ===== END RESPONSE BODY =====`);
|
||||||
|
console.error(`[commit_files] Response body length: ${responseBytes.length} bytes`);
|
||||||
|
console.error(`[commit_files] Response body (first 1000 bytes as hex): ${responseHex}${responseBytes.length > 1000 ? '...' : ''}`);
|
||||||
|
console.error(`[commit_files] Response body (base64): ${responseBase64}`);
|
||||||
|
|
||||||
|
// Try to parse as JSON if it looks like JSON
|
||||||
|
if (responseText.trim().startsWith('{') || responseText.trim().startsWith('[')) {
|
||||||
|
try {
|
||||||
|
const parsedError = JSON.parse(responseText);
|
||||||
|
console.error(`[commit_files] Parsed error object:`, JSON.stringify(parsedError, null, 2));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[commit_files] Response looks like JSON but failed to parse:`, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (readError) {
|
||||||
|
console.error(`[commit_files] CRITICAL: Failed to read error response:`, readError);
|
||||||
|
logDetailedError('commit_files_response_read', readError);
|
||||||
|
responseText = `Failed to read response: ${readError}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(`[commit_files] Update reference error body: "${errorText}"`);
|
// Log memory state after error
|
||||||
console.error(`[commit_files] Error body length: ${errorText?.length}`);
|
const memoryAfter = process.memoryUsage();
|
||||||
console.error(`[commit_files] Error body type: ${typeof errorText}`);
|
console.error(`[commit_files] Memory after error:`, {
|
||||||
|
rss: `${(memoryAfter.rss / 1024 / 1024).toFixed(2)} MB`,
|
||||||
|
heapUsed: `${(memoryAfter.heapUsed / 1024 / 1024).toFixed(2)} MB`,
|
||||||
|
});
|
||||||
|
|
||||||
// Log additional debugging info for 500 errors
|
// Special handling for 500 errors
|
||||||
if (updateRefResponse.status === 500) {
|
if (updateRefResponse.status === 500) {
|
||||||
const requestId = updateRefResponse.headers.get('x-github-request-id');
|
const requestId = updateRefResponse.headers.get('x-github-request-id');
|
||||||
|
console.error(`[commit_files] ===== GITHUB 500 ERROR DETAILS =====`);
|
||||||
console.error(`[commit_files] GitHub Request ID: ${requestId}`);
|
console.error(`[commit_files] GitHub Request ID: ${requestId}`);
|
||||||
console.error(`[commit_files] This appears to be an internal GitHub error`);
|
console.error(`[commit_files] This is an internal GitHub server error`);
|
||||||
console.error(`[commit_files] Token was valid for tree/commit creation but failed for ref update`);
|
console.error(`[commit_files] The error may be transient - consider retrying`);
|
||||||
console.error(`[commit_files] Branch protection rules or permissions might be an issue`);
|
console.error(`[commit_files] Note: Tree (${treeData?.sha}) and commit (${newCommitData?.sha}) were created successfully`);
|
||||||
}
|
console.error(`[commit_files] Only the reference update failed`);
|
||||||
|
console.error(`[commit_files] ===================================`);
|
||||||
// Parse error if it's JSON
|
|
||||||
let parsedError;
|
|
||||||
try {
|
|
||||||
if (errorText && errorText.trim().startsWith('{')) {
|
|
||||||
parsedError = JSON.parse(errorText);
|
|
||||||
console.error(`[commit_files] Parsed error:`, parsedError);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`[commit_files] Error text is not JSON`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to update reference: ${updateRefResponse.status} - ${errorText}`,
|
`Failed to update reference: ${updateRefResponse.status} - ${responseText || 'No response body'}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
261
test-with-new-branch.ts
Normal file
261
test-with-new-branch.ts
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
// Test script that creates a new branch to test the commit_files flow
|
||||||
|
// Run with: bun test-with-new-branch.ts <github-pat> <owner> <repo>
|
||||||
|
|
||||||
|
const GITHUB_API_URL = "https://api.github.com";
|
||||||
|
|
||||||
|
async function testCommitFilesWithNewBranch(token: string, owner: string, repo: string) {
|
||||||
|
const headers = {
|
||||||
|
Accept: "application/vnd.github+json",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a unique branch name for testing
|
||||||
|
const timestamp = Date.now();
|
||||||
|
const testBranch = `claude-debug-500-test-${timestamp}`;
|
||||||
|
|
||||||
|
console.log(`\n=== Testing commit_files flow ===`);
|
||||||
|
console.log(`Repository: ${owner}/${repo}`);
|
||||||
|
console.log(`Test branch: ${testBranch}\n`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// First, get the default branch to branch from
|
||||||
|
console.log("Getting repository info...");
|
||||||
|
const repoResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!repoResponse.ok) {
|
||||||
|
console.error(`Cannot access repository: ${repoResponse.status}`);
|
||||||
|
const error = await repoResponse.text();
|
||||||
|
console.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const repoData = await repoResponse.json();
|
||||||
|
const defaultBranch = repoData.default_branch;
|
||||||
|
console.log(`✓ Default branch: ${defaultBranch}`);
|
||||||
|
|
||||||
|
// Get the SHA of the default branch
|
||||||
|
console.log(`\nGetting ${defaultBranch} branch SHA...`);
|
||||||
|
const defaultBranchResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs/heads/${defaultBranch}`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!defaultBranchResponse.ok) {
|
||||||
|
console.error(`Cannot get default branch: ${defaultBranchResponse.status}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultBranchData = await defaultBranchResponse.json();
|
||||||
|
const baseSha = defaultBranchData.object.sha;
|
||||||
|
console.log(`✓ Base SHA: ${baseSha}`);
|
||||||
|
|
||||||
|
// Create a new branch
|
||||||
|
console.log(`\nCreating test branch: ${testBranch}...`);
|
||||||
|
const createBranchResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
...headers,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
ref: `refs/heads/${testBranch}`,
|
||||||
|
sha: baseSha,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!createBranchResponse.ok) {
|
||||||
|
console.error(`Failed to create branch: ${createBranchResponse.status}`);
|
||||||
|
const error = await createBranchResponse.text();
|
||||||
|
console.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`✓ Created test branch: ${testBranch}`);
|
||||||
|
|
||||||
|
// Now replicate the commit_files flow
|
||||||
|
console.log("\n--- Starting commit_files flow ---");
|
||||||
|
|
||||||
|
// Step 1: Get the branch reference (should be same as baseSha)
|
||||||
|
console.log("\nStep 1: Getting branch reference...");
|
||||||
|
const refUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs/heads/${testBranch}`;
|
||||||
|
const refResponse = await fetch(refUrl, { headers });
|
||||||
|
|
||||||
|
if (!refResponse.ok) {
|
||||||
|
console.error(`Failed: ${refResponse.status}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const refData = await refResponse.json();
|
||||||
|
console.log(`✓ Branch SHA: ${refData.object.sha}`);
|
||||||
|
|
||||||
|
// Step 2: Get the base commit
|
||||||
|
console.log("\nStep 2: Getting base commit...");
|
||||||
|
const commitUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/commits/${baseSha}`;
|
||||||
|
const commitResponse = await fetch(commitUrl, { headers });
|
||||||
|
|
||||||
|
if (!commitResponse.ok) {
|
||||||
|
console.error(`Failed: ${commitResponse.status}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const commitData = await commitResponse.json();
|
||||||
|
const baseTreeSha = commitData.tree.sha;
|
||||||
|
console.log(`✓ Base tree SHA: ${baseTreeSha}`);
|
||||||
|
|
||||||
|
// Step 3: Create a new tree
|
||||||
|
console.log("\nStep 3: Creating new tree...");
|
||||||
|
const treeUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/trees`;
|
||||||
|
|
||||||
|
const testFileContent = `# Test file for debugging 500 error
|
||||||
|
# Created at: ${new Date().toISOString()}
|
||||||
|
# This simulates the commit_files operation from claude-code-action
|
||||||
|
|
||||||
|
def test_function():
|
||||||
|
# This simulates fixing a code issue
|
||||||
|
result = "Fixed code"
|
||||||
|
return result
|
||||||
|
`;
|
||||||
|
|
||||||
|
const treeBody = {
|
||||||
|
base_tree: baseTreeSha,
|
||||||
|
tree: [{
|
||||||
|
path: "test-debug-500.py",
|
||||||
|
mode: "100644",
|
||||||
|
type: "blob",
|
||||||
|
content: testFileContent,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
const treeResponse = await fetch(treeUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
...headers,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(treeBody),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!treeResponse.ok) {
|
||||||
|
console.error(`Failed to create tree: ${treeResponse.status}`);
|
||||||
|
const error = await treeResponse.text();
|
||||||
|
console.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const treeData = await treeResponse.json();
|
||||||
|
console.log(`✓ Tree SHA: ${treeData.sha}`);
|
||||||
|
|
||||||
|
// Step 4: Create commit
|
||||||
|
console.log("\nStep 4: Creating commit...");
|
||||||
|
const newCommitUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/commits`;
|
||||||
|
const commitBody = {
|
||||||
|
message: "Test: Debugging 500 error in commit_files",
|
||||||
|
tree: treeData.sha,
|
||||||
|
parents: [baseSha],
|
||||||
|
};
|
||||||
|
|
||||||
|
const newCommitResponse = await fetch(newCommitUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
...headers,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(commitBody),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!newCommitResponse.ok) {
|
||||||
|
console.error(`Failed to create commit: ${newCommitResponse.status}`);
|
||||||
|
const error = await newCommitResponse.text();
|
||||||
|
console.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCommitData = await newCommitResponse.json();
|
||||||
|
console.log(`✓ Commit SHA: ${newCommitData.sha}`);
|
||||||
|
|
||||||
|
// Step 5: Update reference (this is where the 500 error happens)
|
||||||
|
console.log("\nStep 5: Updating branch reference (the critical step)...");
|
||||||
|
const updateRefUrl = `${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs/heads/${testBranch}`;
|
||||||
|
const updateBody = {
|
||||||
|
sha: newCommitData.sha,
|
||||||
|
force: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`URL: PATCH ${updateRefUrl}`);
|
||||||
|
console.log(`Body: ${JSON.stringify(updateBody)}`);
|
||||||
|
|
||||||
|
const startTime = Date.now();
|
||||||
|
const updateRefResponse = await fetch(updateRefUrl, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
...headers,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(updateBody),
|
||||||
|
});
|
||||||
|
const duration = Date.now() - startTime;
|
||||||
|
|
||||||
|
console.log(`\nStatus: ${updateRefResponse.status} (took ${duration}ms)`);
|
||||||
|
console.log(`Headers:`, {
|
||||||
|
'x-ratelimit-remaining': updateRefResponse.headers.get('x-ratelimit-remaining'),
|
||||||
|
'x-github-request-id': updateRefResponse.headers.get('x-github-request-id'),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updateRefResponse.ok) {
|
||||||
|
console.error(`\n✗ FAILED: ${updateRefResponse.status}`);
|
||||||
|
const errorText = await updateRefResponse.text();
|
||||||
|
console.error(`Error body: "${errorText}"`);
|
||||||
|
|
||||||
|
if (updateRefResponse.status === 500) {
|
||||||
|
console.error(`\n🔍 500 ERROR REPRODUCED!`);
|
||||||
|
console.error(`This confirms the issue exists with PAT as well.`);
|
||||||
|
console.error(`GitHub Request ID: ${updateRefResponse.headers.get('x-github-request-id')}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`\n✓ SUCCESS: Branch updated!`);
|
||||||
|
console.log(`The 500 error might be specific to certain conditions.`);
|
||||||
|
|
||||||
|
// Cleanup: delete the test branch
|
||||||
|
console.log(`\nCleaning up test branch...`);
|
||||||
|
const deleteResponse = await fetch(
|
||||||
|
`${GITHUB_API_URL}/repos/${owner}/${repo}/git/refs/heads/${testBranch}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (deleteResponse.ok) {
|
||||||
|
console.log(`✓ Test branch deleted`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`\nUnexpected error:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main execution
|
||||||
|
const [token, owner, repo] = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (!token || !owner || !repo) {
|
||||||
|
console.log("Usage: bun test-with-new-branch.ts <github-pat> <owner> <repo>");
|
||||||
|
console.log("");
|
||||||
|
console.log("Examples:");
|
||||||
|
console.log(" bun test-with-new-branch.ts ghp_xxx myorg myrepo");
|
||||||
|
console.log(" bun test-with-new-branch.ts ghp_xxx anthropics anthropic");
|
||||||
|
console.log("");
|
||||||
|
console.log("This creates a test branch and replicates the commit_files flow.");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Starting test with new branch...");
|
||||||
|
testCommitFilesWithNewBranch(token, owner, repo);
|
||||||
Reference in New Issue
Block a user