217 lines
8.6 KiB
YAML
217 lines
8.6 KiB
YAML
name: PR Walkthrough Reconcile
|
|
|
|
# A description edit must be able to repair a stale callout without starting
|
|
# the model-backed generation workflow or executing pull request code.
|
|
on:
|
|
pull_request_target:
|
|
types: [edited]
|
|
|
|
permissions: {}
|
|
|
|
concurrency:
|
|
group: pr-walkthrough-reconcile-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
pr-walkthrough-reconcile:
|
|
if: >
|
|
vars.PR_WALKTHROUGH_ENABLED == 'true' &&
|
|
github.event_name == 'pull_request_target' &&
|
|
github.event.action == 'edited' &&
|
|
github.event.pull_request.draft == false &&
|
|
(
|
|
github.event.pull_request.head.repo.full_name == github.repository ||
|
|
(
|
|
github.event.pull_request.head.repo.full_name != github.repository &&
|
|
(
|
|
contains(github.event.pull_request.labels.*.name, 'safe-to-review') ||
|
|
(vars.CLAUDE_REVIEW_ALLOWLIST != '' &&
|
|
contains(fromJSON(vars.CLAUDE_REVIEW_ALLOWLIST), github.event.pull_request.user.login))
|
|
)
|
|
)
|
|
)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
concurrency:
|
|
group: pr-description-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: false
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
|
|
steps:
|
|
- name: Checkout trusted reconciliation helper
|
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
with:
|
|
ref: ${{ github.workflow_sha }}
|
|
fetch-depth: 1
|
|
persist-credentials: false
|
|
|
|
- name: Repair stale walkthrough callout
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
TRUSTED_SHA: ${{ github.workflow_sha }}
|
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
WALKTHROUGH_BASE_URL: https://walkthrough.kandev.ai
|
|
run: |
|
|
set -euo pipefail
|
|
test "$(git rev-parse HEAD)" = "$TRUSTED_SHA"
|
|
test -f scripts/pr-walkthrough-pr-body
|
|
test "$PR_NUMBER" -gt 0
|
|
[[ "$HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]
|
|
|
|
SHORT_HEAD_SHA="${HEAD_SHA:0:12}"
|
|
PUBLIC_URL="${WALKTHROUGH_BASE_URL%/}/pr/${PR_NUMBER}/${SHORT_HEAD_SHA}.html"
|
|
RESPONSE_META_PATH=pr-walkthrough-public.response
|
|
PUBLIC_HTML_PATH=pr-walkthrough-public.html
|
|
|
|
if ! curl --fail --silent --show-error --location --max-time 30 \
|
|
--retry 3 --retry-all-errors --retry-delay 2 --retry-max-time 30 \
|
|
--output "$PUBLIC_HTML_PATH" \
|
|
--write-out '%{http_code}\t%{content_type}\t%{url_effective}' \
|
|
> "$RESPONSE_META_PATH" \
|
|
"$PUBLIC_URL"; then
|
|
echo "Canonical walkthrough is not publicly available; skipping repair." >> "$GITHUB_STEP_SUMMARY"
|
|
exit 0
|
|
fi
|
|
IFS=$'\t' read -r response_status response_content_type response_effective_url < "$RESPONSE_META_PATH"
|
|
if [ "$response_status" != "200" ] ||
|
|
[ "$response_effective_url" != "$PUBLIC_URL" ] ||
|
|
! printf '%s\n' "$response_content_type" | grep -Eiq '^text/html(;|[[:space:]]|$)' ||
|
|
! test -s "$PUBLIC_HTML_PATH"; then
|
|
echo "Canonical walkthrough did not return a non-empty HTML response; skipping repair." >> "$GITHUB_STEP_SUMMARY"
|
|
exit 0
|
|
fi
|
|
|
|
for attempt in 1 2 3; do
|
|
rm -f \
|
|
pr-response.json \
|
|
pr-latest-response.json \
|
|
pr-verified-response.json \
|
|
pr-update.json \
|
|
pr-verify-update.json \
|
|
pr-body-before \
|
|
pr-body-latest \
|
|
pr-body-expected \
|
|
pr-body-verified
|
|
|
|
if ! gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" > pr-response.json ||
|
|
! python3 -c 'import json; json.load(open("pr-response.json", encoding="utf-8"))'; then
|
|
if [ "$attempt" -eq 3 ]; then
|
|
echo "Unable to fetch the pull request description." >&2
|
|
exit 1
|
|
fi
|
|
sleep 2
|
|
continue
|
|
fi
|
|
|
|
set +e
|
|
python3 scripts/pr-walkthrough-pr-body \
|
|
--github-response pr-response.json \
|
|
--output pr-update.json \
|
|
--url "$PUBLIC_URL" \
|
|
--pr-number "$PR_NUMBER" \
|
|
--head-sha "$HEAD_SHA" \
|
|
--require-existing
|
|
update_status=$?
|
|
set -e
|
|
|
|
if [ "$update_status" -eq 4 ]; then
|
|
echo "No existing walkthrough callout found; skipping repair." >> "$GITHUB_STEP_SUMMARY"
|
|
exit 0
|
|
fi
|
|
if [ "$update_status" -eq 3 ]; then
|
|
echo "PR description already links the canonical walkthrough." >> "$GITHUB_STEP_SUMMARY"
|
|
exit 0
|
|
fi
|
|
if [ "$update_status" -ne 0 ]; then
|
|
echo "Existing walkthrough markers are malformed; refusing to rewrite the PR description." >&2
|
|
exit 1
|
|
fi
|
|
jq -j '.body // ""' pr-update.json > pr-body-expected
|
|
|
|
jq -j '.body // ""' pr-response.json > pr-body-before
|
|
if ! gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" > pr-latest-response.json ||
|
|
! python3 -c 'import json; json.load(open("pr-latest-response.json", encoding="utf-8"))'; then
|
|
if [ "$attempt" -eq 3 ]; then
|
|
echo "Unable to refresh the pull request description." >&2
|
|
exit 1
|
|
fi
|
|
sleep 2
|
|
continue
|
|
fi
|
|
current_head_sha="$(jq -r '.head.sha // ""' pr-latest-response.json)"
|
|
if [ "$current_head_sha" != "$HEAD_SHA" ]; then
|
|
echo "PR head advanced to ${current_head_sha}; skipping stale repair." >> "$GITHUB_STEP_SUMMARY"
|
|
exit 0
|
|
fi
|
|
jq -j '.body // ""' pr-latest-response.json > pr-body-latest
|
|
if ! cmp -- pr-body-before pr-body-latest; then
|
|
echo "Pull request description changed before repair PATCH; retrying." >&2
|
|
sleep 2
|
|
continue
|
|
fi
|
|
|
|
if ! gh api --method PATCH \
|
|
"repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" \
|
|
--input pr-update.json \
|
|
--silent; then
|
|
if [ "$attempt" -eq 3 ]; then
|
|
echo "Unable to repair the pull request description." >&2
|
|
exit 1
|
|
fi
|
|
sleep 2
|
|
continue
|
|
fi
|
|
|
|
if ! gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" > pr-verified-response.json ||
|
|
! python3 -c 'import json; json.load(open("pr-verified-response.json", encoding="utf-8"))'; then
|
|
if [ "$attempt" -eq 3 ]; then
|
|
echo "Unable to verify the repaired pull request description." >&2
|
|
exit 1
|
|
fi
|
|
sleep 2
|
|
continue
|
|
fi
|
|
verified_head_sha="$(jq -r '.head.sha // ""' pr-verified-response.json)"
|
|
if [ "$verified_head_sha" != "$HEAD_SHA" ]; then
|
|
echo "PR head advanced to ${verified_head_sha}; skipping stale repair." >> "$GITHUB_STEP_SUMMARY"
|
|
exit 0
|
|
fi
|
|
jq -j '.body // ""' pr-verified-response.json > pr-body-verified
|
|
if ! cmp -- pr-body-expected pr-body-verified; then
|
|
if [ "$attempt" -eq 3 ]; then
|
|
echo "Pull request description changed before repair readback verification." >&2
|
|
exit 1
|
|
fi
|
|
echo "Pull request description changed after repair PATCH; retrying." >&2
|
|
sleep 2
|
|
continue
|
|
fi
|
|
|
|
set +e
|
|
python3 scripts/pr-walkthrough-pr-body \
|
|
--github-response pr-verified-response.json \
|
|
--output pr-verify-update.json \
|
|
--url "$PUBLIC_URL" \
|
|
--pr-number "$PR_NUMBER" \
|
|
--head-sha "$HEAD_SHA" \
|
|
--require-existing
|
|
verify_status=$?
|
|
set -e
|
|
if [ "$verify_status" -eq 3 ]; then
|
|
echo "Repaired the stale walkthrough link in the PR description." >> "$GITHUB_STEP_SUMMARY"
|
|
exit 0
|
|
fi
|
|
if [ "$attempt" -eq 3 ]; then
|
|
echo "Pull request description readback did not contain the canonical walkthrough." >&2
|
|
exit 1
|
|
fi
|
|
echo "Pull request description readback changed; retrying repair." >&2
|
|
sleep 2
|
|
done
|
|
|
|
echo "Pull request description repair did not converge." >&2
|
|
exit 1
|