Fix PR Review Automation for cross-repository PRs (#571)

* Add PR review automation workflow and script

closes #559

* Improve PR review automation with better error handling and permissions

* Update PR review automation to use pull_request_target event for improved security

* Fix PR automation to handle cross-repo references correctly

- Use SHA instead of ref names to avoid 404 errors
- Handle head content from source repo and base from target repo
- Add better error handling and debugging info
- Prevent workflow failures when API check fails
This commit is contained in:
Juan Diaz
2025-07-11 17:01:56 -04:00
committed by GitHub
parent c28cab177f
commit 8dbb961dde

View File

@@ -15,6 +15,10 @@ async function run() {
console.log(`PR author: ${pr.user.login}`); console.log(`PR author: ${pr.user.login}`);
console.log(`Head repo: ${pr.head.repo.full_name}`); console.log(`Head repo: ${pr.head.repo.full_name}`);
console.log(`Base repo: ${pr.base.repo.full_name}`); console.log(`Base repo: ${pr.base.repo.full_name}`);
console.log(`Head SHA: ${pr.head.sha}`);
console.log(`Base SHA: ${pr.base.sha}`);
console.log(`Head ref: ${pr.head.ref}`);
console.log(`Base ref: ${pr.base.ref}`);
const filesChanged = await octokit.rest.pulls.listFiles({ const filesChanged = await octokit.rest.pulls.listFiles({
owner, owner,
@@ -82,47 +86,55 @@ async function run() {
} }
async function checkForNewApiLinks(owner, repo, pr) { async function checkForNewApiLinks(owner, repo, pr) {
const baseRes = await octokit.rest.repos.getContent({ try {
owner, // For pull_request_target, we need to get content from the correct repositories
repo, // Base content from the target repository (upstream)
path: "README.md", const baseRes = await octokit.rest.repos.getContent({
ref: pr.base.ref, owner,
}); repo,
path: "README.md",
ref: pr.base.sha, // Use SHA instead of ref name
});
const headRes = await octokit.rest.repos.getContent({ // Head content from the source repository (could be a fork)
owner, const headRes = await octokit.rest.repos.getContent({
repo, owner: pr.head.repo.owner.login,
path: "README.md", repo: pr.head.repo.name,
ref: pr.head.ref, path: "README.md",
}); ref: pr.head.sha, // Use SHA instead of ref name
});
const decode = (res) => const decode = (res) =>
Buffer.from(res.data.content, "base64").toString("utf8"); Buffer.from(res.data.content, "base64").toString("utf8");
const baseContent = decode(baseRes); const baseContent = decode(baseRes);
const headContent = decode(headRes); const headContent = decode(headRes);
const baseLinks = new Set( const baseLinks = new Set(
[...baseContent.matchAll(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g)].map( [...baseContent.matchAll(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g)].map(
(m) => m[2] (m) => m[2]
) )
); );
const headLinks = new Set( const headLinks = new Set(
[...headContent.matchAll(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g)].map( [...headContent.matchAll(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g)].map(
(m) => m[2] (m) => m[2]
) )
); );
const newLinks = [...headLinks].filter((link) => !baseLinks.has(link)); const newLinks = [...headLinks].filter((link) => !baseLinks.has(link));
console.log(`Base links found: ${baseLinks.size}`); console.log(`Base links found: ${baseLinks.size}`);
console.log(`Head links found: ${headLinks.size}`); console.log(`Head links found: ${headLinks.size}`);
console.log(`New links found: ${newLinks.length}`); console.log(`New links found: ${newLinks.length}`);
if (newLinks.length > 0) { if (newLinks.length > 0) {
console.log("New links:", newLinks); console.log("New links:", newLinks);
}
return newLinks;
} catch (error) {
console.error("Error checking for new API links:", error);
return []; // Return empty array on error to avoid breaking the workflow
} }
return newLinks;
} }
run().catch((error) => { run().catch((error) => {