From 271c5d8a748cc77ce7dceacfaa08115c560ed642 Mon Sep 17 00:00:00 2001 From: anupsdf Date: Tue, 5 May 2026 16:30:25 -0700 Subject: [PATCH] Github workflow to check for CAP status on stellar-core PRs This reverts commit 9b29a9b496dc537501b64ba893aecb7321cb2a15. fix link --- .github/scripts/check-cap-status.sh | 106 +++++++++++++++++++++++++ .github/workflows/cap-status-check.yml | 53 +++++++++++++ 2 files changed, 159 insertions(+) create mode 100755 .github/scripts/check-cap-status.sh create mode 100644 .github/workflows/cap-status-check.yml diff --git a/.github/scripts/check-cap-status.sh b/.github/scripts/check-cap-status.sh new file mode 100755 index 000000000..e94e105b7 --- /dev/null +++ b/.github/scripts/check-cap-status.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# +# Scans a PR's diff for newly added `#ifdef CAP_XXXX` / `defined(CAP_XXXX)` +# references in C/C++ source files under src/ and verifies each referenced +# CAP is in "Accepted" status on stellar/stellar-protocol@master. Posts (or +# updates) a single warning comment on the PR if any are not Accepted. +# +# Soft warning only: always exits 0 so it never blocks merges. +# +# Required env: +# BASE_SHA base ref SHA to diff against +# HEAD_SHA PR head SHA +# PR_NUMBER PR number for commenting +# REPO owner/name of the repo (for gh api comment update) +# GH_TOKEN token with pull-requests: write +set -euo pipefail + +MARKER="" +PROTOCOL_BASE="https://raw.githubusercontent.com/stellar/stellar-protocol/master/core" +PROTOCOL_VIEW="https://github.com/stellar/stellar-protocol/blob/master/core" + +# Extract CAP_NNNN identifiers from lines added by this PR in C/C++ source +# files under src/. Uses awk to track the current file from `diff --git` +# headers so we only emit matches from qualifying paths. +mapfile -t CAPS < <( + git diff --unified=0 --diff-filter=AM "${BASE_SHA}" "${HEAD_SHA}" -- '*.h' '*.hpp' '*.cpp' \ + | awk ' + /^diff --git/ { + # path is the "b/..." side + file = $0 + sub(/.* b\//, "", file) + ok = (file ~ /^src\/.*\.(h|hpp|cpp)$/) + next + } + ok && /^\+/ && !/^\+\+\+/ { + s = $0 + while (match(s, /CAP_[0-9]{4}/)) { + print substr(s, RSTART, RLENGTH) + s = substr(s, RSTART + RLENGTH) + } + } + ' \ + | sort -u +) + +if [ "${#CAPS[@]}" -eq 0 ]; then + echo "No CAP_XXXX references added in this PR." + exit 0 +fi + +echo "Found CAP references in diff: ${CAPS[*]}" + +NON_ACCEPTED="" +for cap in "${CAPS[@]}"; do + num="${cap#CAP_}" + url="${PROTOCOL_BASE}/cap-${num}.md" + view_url="${PROTOCOL_VIEW}/cap-${num}.md" + + http_code=$(curl -sSL -o /tmp/cap.md -w '%{http_code}' "$url" || echo "000") + if [ "$http_code" != "200" ]; then + NON_ACCEPTED+="- \`${cap}\`: CAP markdown not found at [${url}](${url}) (HTTP ${http_code})"$'\n' + continue + fi + + # Status line lives in the leading code-fenced preamble. Grab the first + # `Status:` line and strip the prefix + surrounding whitespace. + status=$(grep -m1 -E '^Status:' /tmp/cap.md | sed -E 's/^Status:[[:space:]]*//; s/[[:space:]]+$//') + + if [ "$status" != "Accepted" ]; then + NON_ACCEPTED+="- \`${cap}\`: status is **${status:-unknown}** ([cap-${num}.md](${view_url}))"$'\n' + fi +done + +if [ -z "$NON_ACCEPTED" ]; then + echo "All referenced CAPs are Accepted." + exit 0 +fi + +COMMENT=$(cat </dev/null +else + echo "Posting new warning comment" + gh pr comment "$PR_NUMBER" --repo "$REPO" --body "$COMMENT" +fi + +exit 0 diff --git a/.github/workflows/cap-status-check.yml b/.github/workflows/cap-status-check.yml new file mode 100644 index 000000000..cd7d07e1b --- /dev/null +++ b/.github/workflows/cap-status-check.yml @@ -0,0 +1,53 @@ +# Soft check that any `#ifdef CAP_XXXX` / `defined(CAP_XXXX)` reference added +# in a PR points to a CAP whose status on stellar/stellar-protocol@master is +# "Accepted". Posts an advisory PR comment when a non-Accepted CAP is guarded; +# never fails the check (exit 0 always). +# +# Uses `pull_request_target` so the comment-posting token works for PRs opened +# from forks. The job intentionally does NOT execute any code from the PR head: +# it only runs `git diff` over PR-head blobs (data, not execution) using the +# trusted base checkout's git binary and config. Treat any future step that +# runs, sources, or interprets PR-head files as a security-relevant change. +name: CAP Status Check + +on: + pull_request_target: + types: [opened, synchronize, ready_for_review, reopened] + paths: + - 'src/**/*.h' + - 'src/**/*.hpp' + - 'src/**/*.cpp' + +permissions: {} + +concurrency: + group: cap-status-check-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + # Check out the base ref (trusted code from this repo) so that the + # workflow's scripts and git config come from base, not the PR. + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.base.sha }} + + # Fetch the PR head as a bare ref so `git diff` can read its blobs. + # `pull//head` works for both same-repo and fork PRs. + - name: Fetch PR head + run: git fetch --no-tags origin "pull/${{ github.event.pull_request.number }}/head" + + - name: Check CAP statuses on diff + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + run: .github/scripts/check-cap-status.sh