mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-22 22:44:13 +08:00
- 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
183 lines
6.3 KiB
TypeScript
183 lines
6.3 KiB
TypeScript
#!/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); |