Skip to content

Stale Autogenerated Docs PRs #125

Stale Autogenerated Docs PRs

Stale Autogenerated Docs PRs #125

# Detects autogenerated docs PRs that failed to merge via auto-merge-cron.
#
# All four autogenerated docs workflows (agent-sdk-docs-generate,
# pyairbyte-docs-generate, sync-ai-connector-docs, and
# regenerate-agent-engine-api-spec) create PRs with the labels
# `auto-merge` and `auto-generated`. The auto-merge-cron workflow
# should merge them within ~2 hours. If a PR is still open after
# 4 hours, something went wrong and a human should be notified.
name: Stale Autogenerated Docs PRs
permissions:
contents: read
on:
schedule:
# Every 6 hours, offset from auto-merge-cron (which runs on the hour).
- cron: "30 */6 * * *"
workflow_dispatch:
jobs:
detect-stale:
name: Detect stale autogenerated docs PRs
runs-on: ubuntu-24.04
steps:
# ---------- Authentication ----------
- name: Authenticate as GitHub App
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
id: get-app-token
with:
owner: "airbytehq"
repositories: "airbyte"
app-id: ${{ secrets.OCTAVIA_BOT_HOARD_APP_ID }}
private-key: ${{ secrets.OCTAVIA_BOT_HOARD_PRIVATE_KEY }}
# ---------- Find stale PRs ----------
- name: Find stale autogenerated docs PRs
id: find-stale
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ steps.get-app-token.outputs.token }}
script: |
const STALE_THRESHOLD_HOURS = 4;
const now = Date.now();
// Use Issues API for server-side label filtering (pulls.list
// doesn't support labels, so it would paginate every open PR).
const issues = await github.paginate(
github.rest.issues.listForRepo,
{
owner: 'airbytehq',
repo: 'airbyte',
state: 'open',
labels: 'auto-merge,auto-generated',
per_page: 100,
},
);
// Issues API returns both issues and PRs; keep only PRs.
const prs = issues.filter((i) => i.pull_request);
const stale = prs.filter((pr) => {
const labels = pr.labels.map((l) => l.name);
if (labels.includes('stale-notified')) {
return false;
}
const ageMs = now - new Date(pr.created_at).getTime();
return ageMs > STALE_THRESHOLD_HOURS * 3600 * 1000;
});
core.info(`Found ${stale.length} stale autogenerated docs PR(s).`);
const matrix = stale.map((pr) => ({
number: pr.number,
title: pr.title,
created_at: pr.created_at,
url: pr.html_url,
}));
core.setOutput('matrix', JSON.stringify(matrix));
core.setOutput('count', String(stale.length));
# ---------- Notify then label each stale PR ----------
# Label is applied *after* a successful notification dispatch so
# that a failed dispatch is retried on the next cycle.
- name: Notify and label stale PRs
if: fromJSON(steps.find-stale.outputs.count) > 0
env:
GH_TOKEN: ${{ steps.get-app-token.outputs.token }}
STALE_MATRIX: ${{ steps.find-stale.outputs.matrix }}
GITHUB_CI_WORKFLOW_TRIGGER_PAT: ${{ secrets.GITHUB_CI_WORKFLOW_TRIGGER_PAT }}
WORKFLOW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
NOTIFY_TARGET: ${{ vars.STALE_DOCS_NOTIFY_TARGET || 'ian.alton@airbyte.io' }}
run: |
set -euo pipefail
echo "$STALE_MATRIX" | jq -c '.[]' | while read -r pr_json; do
pr_number=$(echo "$pr_json" | jq -r '.number')
pr_title=$(echo "$pr_json" | jq -r '.title')
pr_url=$(echo "$pr_json" | jq -r '.url')
pr_created=$(echo "$pr_json" | jq -r '.created_at')
age_seconds=$(( $(date +%s) - $(date -d "$pr_created" +%s) ))
age_hours=$(( age_seconds / 3600 ))
message="*Stale autogenerated docs PR detected*"
message+=$'\n\n'
message+="PR <${pr_url}|#${pr_number}>: ${pr_title}"
message+=$'\n'
message+="This pull request was created ${age_hours}h ago. I expected merge via \`auto-merge-cron\` within two hours, but the pull request is still open."
message+=$'\n\n'
message+="Investigate and repair this PR and, if necessary, the root cause of its failure to merge."
message+=$'\n'
message+="Most likely causes: CI check failure on the PR, a merge conflict with master,"
message+=" a path outside the \`auto-merge-cron\` allowlist (see \`validate-paths\` in \`auto-merge-cron.yml\`),"
message+=" or \`auto-merge-cron\` itself failing to run (check recent runs of the \`Auto Merge\` workflow)."
message+=$'\n'
message+="<${WORKFLOW_RUN_URL}|View detection run>"
curl -sSf -X POST \
-H "Authorization: token ${GITHUB_CI_WORKFLOW_TRIGGER_PAT}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/airbytehq/airbyte-ops-mcp/actions/workflows/human-in-the-loop.yml/dispatches" \
-d "$(jq -n \
--arg message "$message" \
--arg pr_url "$pr_url" \
--arg session_url "$WORKFLOW_RUN_URL" \
--arg target "$NOTIFY_TARGET" \
'{
ref: "main",
inputs: {
target_person: $target,
message: $message,
agent_session_url: $session_url,
pr_url: $pr_url,
header_emoji: ":warning:",
header_label: "Stale Autogenerated Docs PR"
}
}'
)"
# Label only after successful dispatch so failures retry next cycle.
gh pr edit "$pr_number" --repo airbytehq/airbyte --add-label "stale-notified"
echo "::notice::Notified for stale PR #${pr_number} (${age_hours}h old)"
done