fix(ci): read icon metadata at the reviewed commit

The review comments are anchored to the commit that triggered the run,
but the icon JSON was read through the PR files' raw_url, which always
points at the latest head. When a commit was pushed while the action
was running, suggestions were anchored to the wrong lines, e.g. a
use-cases block replacing the "tags": [ line, producing invalid JSON.

Read each file at the reviewed commit instead, default that commit to the
PR's head SHA, and skip files that don't exist at it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSdUq9uFpjuQsoobCBqz4j
This commit is contained in:
Claude
2026-10-23 16:28:50 +00:00
parent 49161aba73
commit c7bfa44b77
@@ -34,7 +34,6 @@ const {
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const pullRequestNumber = Number(process.env.PULL_REQUEST_NUMBER);
const username = process.env.REVIEWER ?? 'github-actions[bot]';
const commitSha = process.env.COMMIT_SHA ?? "HEAD";
const useFileSystem = process.env.USE_FILE_SYSTEM === 'true' || false;
const cacheFile = process.env.SUGGESTIONS_CACHE_FILE;
@@ -151,6 +150,10 @@ const { data: pullRequest } = await octokit.pulls.get({
const prDescription = (pullRequest.body || '').slice(0, 4000);
// The commit the review comments are anchored to. Icon files are read at this
// commit too, so line numbers match even if new commits are pushed mid-run.
const commitSha = process.env.COMMIT_SHA || pullRequest.head.sha;
// The action runs on every update of the pull request. Collect the values that
// were already suggested per file and field from the suggestion blocks of
// earlier comments, so they are not suggested again. Comparing values rather
@@ -333,22 +336,32 @@ async function getSuggestion(
return suggested;
}
const suggestionsByFile = await Promise.all(
changedFiles.map(async ({ filename, raw_url }) => {
const suggestionsByFile = (await Promise.all(
changedFiles.map(async ({ filename }) => {
const iconName = path.basename(filename, '.json');
// Read the icon's current metadata (the PR head version) first, so we can
// Read the icon's current metadata at the reviewed commit first, so we can
// both give it to the model as context and dedupe suggestions against it.
let fileContent: string
if (useFileSystem) {
fileContent = await fs.readFile(path.join(repoRoot, filename), 'utf-8');
} else {
const fileGithubRequest = await octokit.request(`GET ${raw_url}`, {
headers: {
'Accept': 'application/vnd.github.v3.raw',
},
});
fileContent = fileGithubRequest.data
try {
const { data } = await octokit.repos.getContent({
owner,
repo,
path: filename,
ref: commitSha,
mediaType: { format: 'raw' },
});
fileContent = data as unknown as string;
} catch (error) {
// The file list is for the latest head, so a file can be missing at
// the reviewed commit (added later, or removed). A later run covers it.
if ((error as { status?: number }).status !== 404) throw error;
console.log(`${filename} does not exist at ${commitSha}. Skipping...`);
return null;
}
}
const metadata = JSON.parse(fileContent)
@@ -366,7 +379,7 @@ const suggestionsByFile = await Promise.all(
return { filename, iconName, fileContent, currentMetadata, suggested };
}),
);
)).filter((file) => file !== null);
// Write the cache before any early exit below, so the action can save it.
if (cacheFile) {