fix: identify submitted server version in publish diagnostics #111
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Detect invalid publish PRs | |
| # Stage 1 of 2 (see close-invalid-publish-prs.yml for stage 2). | |
| # | |
| # Some contributors try to "publish" a server by opening a PR that adds files | |
| # under servers/ or edits data/seed.json. That is not how publishing works - | |
| # servers are published with the mcp-publisher CLI against the live registry | |
| # API. This job only DETECTS such PRs. It runs from the (untrusted) PR context | |
| # with a read-only token, so it cannot comment on or close anything. When it | |
| # finds a match it records just the PR number as an artifact; the privileged | |
| # stage-2 workflow picks that up via workflow_run and acts on it. | |
| # | |
| # We never check out or run the PR's code - we only read changed file paths. | |
| on: | |
| pull_request: | |
| types: [opened, reopened] | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| jobs: | |
| detect: | |
| runs-on: ubuntu-latest | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association }} | |
| steps: | |
| - name: Classify PR | |
| id: classify | |
| run: | | |
| set -euo pipefail | |
| match=false | |
| # Maintainers may legitimately touch these files (e.g. dev seed data), | |
| # so never flag their PRs. | |
| case "$AUTHOR_ASSOCIATION" in | |
| MEMBER|OWNER|COLLABORATOR) | |
| echo "Author association '$AUTHOR_ASSOCIATION' is trusted; not flagging." | |
| ;; | |
| *) | |
| # Use the paginated REST endpoint: `gh pr view --json files` caps | |
| # at 100 files with no pagination, which could truncate a large PR | |
| # and cause a false match. | |
| mapfile -t FILES < <(gh api --paginate "repos/$GH_REPO/pulls/$PR_NUMBER/files" --jq '.[].filename') | |
| if [ "${#FILES[@]}" -gt 0 ]; then | |
| # Flag only if the PR touches *exclusively* publish-attempt files | |
| # and nothing else, so that legit PRs touching seed data | |
| # alongside real code are left alone. | |
| # | |
| # Keep this list in sync with the copy in stage 2. It stays an | |
| # explicit list rather than a broad `**/server.json` glob: | |
| # because we require *every* file to match, an over-broad | |
| # pattern would auto-close a legitimate single-file PR. | |
| # data/servers/** is here because #1524 put its server.json | |
| # there - a natural guess next to data/seed.json - and slipped | |
| # through the original servers/**-only list. | |
| publish_attempt=0 | |
| other=0 | |
| for f in "${FILES[@]}"; do | |
| if [[ "$f" == servers/* \ | |
| || "$f" == data/servers/* \ | |
| || "$f" == data/data/servers/* \ | |
| || "$f" =~ ^data/[^/]*server\.json$ \ | |
| || "$f" == "data/seed.json" \ | |
| || "$f" == "server.json" \ | |
| || "$f" == "servers.json" ]]; then | |
| publish_attempt=1 | |
| else | |
| echo "Non-publish file changed: $f" | |
| other=1 | |
| fi | |
| done | |
| if [ "$publish_attempt" -eq 1 ] && [ "$other" -eq 0 ]; then | |
| match=true | |
| fi | |
| fi | |
| ;; | |
| esac | |
| echo "PR #$PR_NUMBER match=$match" | |
| echo "match=$match" >> "$GITHUB_OUTPUT" | |
| if [ "$match" = "true" ]; then | |
| echo "$PR_NUMBER" > pr-number.txt | |
| fi | |
| - name: Record flagged PR number | |
| if: steps.classify.outputs.match == 'true' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4 | |
| with: | |
| name: flagged-pr | |
| path: pr-number.txt | |
| retention-days: 1 |