Files
claude-code/.github/workflows/lock-closed-issues.yml
Ashwin Bhat baf38ddaaa Fix lock-closed-issues workflow: use search API instead of offset pagination (#69470)
The workflow has been failing daily since 2026-04-27 with HTTP 422
"Pagination with the page parameter is not supported for large
datasets" at page=100. The repo now has ~58k closed issues and the
script was paging past ~10k already-locked ones every run before
reaching any candidates.

Replace listForRepo + page=N with the search API
(is:issue is:closed is:unlocked updated:<cutoff), which returns only
the issues that actually need locking. Cap at 250/run with a 1s sleep
between locks to stay under secondary rate limits.

Claude-Session: https://claude.ai/code/session_016EWY3FKCJyfUdCAZkXfi7i
2026-06-18 17:15:49 -07:00

78 lines
2.7 KiB
YAML

name: "Lock Stale Issues"
on:
schedule:
# 8am Pacific = 1pm UTC (2pm UTC during DST)
- cron: "0 14 * * *"
workflow_dispatch:
permissions:
issues: write
concurrency:
group: lock-threads
jobs:
lock-closed-issues:
runs-on: ubuntu-latest
steps:
- name: Lock closed issues after 7 days of inactivity
uses: actions/github-script@v7
with:
script: |
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const cutoff = sevenDaysAgo.toISOString().split('T')[0];
const lockComment = `This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.`;
const query = `repo:${context.repo.owner}/${context.repo.repo} is:issue is:closed is:unlocked updated:<${cutoff}`;
console.log(`Search query: ${query}`);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const MAX_PER_RUN = 250;
const processed = new Set();
let totalLocked = 0;
while (totalLocked < MAX_PER_RUN) {
const { data } = await github.rest.search.issuesAndPullRequests({
q: query,
sort: 'updated',
order: 'asc',
per_page: 100,
});
if (totalLocked === 0) {
console.log(`Total candidates: ${data.total_count}`);
}
const fresh = data.items.filter((i) => !processed.has(i.number));
if (fresh.length === 0) break;
for (const issue of fresh) {
if (totalLocked >= MAX_PER_RUN) break;
processed.add(issue.number);
try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: lockComment,
});
await github.rest.issues.lock({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
lock_reason: 'resolved',
});
totalLocked++;
console.log(`Locked issue #${issue.number}: ${issue.title}`);
await sleep(1000);
} catch (error) {
console.error(`Failed to lock issue #${issue.number}: ${error.message}`);
}
}
}
console.log(`Total issues locked: ${totalLocked}`);