Split prize index into adoption-first vs legacy schemes #506
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: Validate Submission | |
| # `pull_request_target` is used so the workflow can comment on PRs from forks | |
| # (which is how solutions are submitted). It runs on the base branch with | |
| # write perms, so the fork's tree is never checked out here: the only | |
| # submission content the validator reads is fetched by path as data. | |
| on: | |
| pull_request_target: | |
| types: [opened, synchronize, reopened, edited] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| # Cancel an in-flight run for the same PR when a new commit is pushed. | |
| concurrency: | |
| group: validate-submission-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| validate: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout base branch (trusted) | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.pull_request.base.sha }} | |
| path: base | |
| # Nothing in this job pushes, so the token has no reason to sit in | |
| # base/.git/config for the life of the run. | |
| persist-credentials: false | |
| - name: Get changed files | |
| id: changed | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| run: | | |
| # API is used instead of `git diff` to avoid shallow-checkout merge-base issues. | |
| files=$(gh api --paginate \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "/repos/${{ github.repository }}/pulls/${PR_NUMBER}/files" \ | |
| --jq '.[].filename') | |
| { | |
| echo "files<<__LP_EOF__" | |
| echo "$files" | |
| echo "__LP_EOF__" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Fetch changed solution files (untrusted data, never executed) | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| REPO: ${{ github.repository }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| CHANGED_FILES: ${{ steps.changed.outputs.files }} | |
| run: | | |
| # The validator reads exactly one thing from the PR: the submission | |
| # markdown. Fetching those blobs by path keeps the fork's working | |
| # tree off the runner entirely, so there is nothing here to execute | |
| # and no `allow-unsafe-pr-checkout` opt-in to carry. Paths come from | |
| # the PR files API and are constrained to a single markdown file | |
| # directly under `solutions/`. | |
| mkdir -p pr/solutions | |
| solution_files=$(printf '%s\n' "$CHANGED_FILES" \ | |
| | grep -E '^solutions/[A-Za-z0-9._-]+\.md$' || true) | |
| # Unquoted on purpose: the pattern above admits no spaces or globs. | |
| for f in $solution_files; do | |
| if ! gh api -H "Accept: application/vnd.github.raw" \ | |
| "/repos/${REPO}/contents/${f}?ref=${HEAD_SHA}" > "pr/${f}"; then | |
| rm -f "pr/${f}" | |
| echo "::warning::Could not fetch ${f} at ${HEAD_SHA}." | |
| fi | |
| done | |
| - name: Run validation | |
| id: validate | |
| env: | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| PR_REPO: ${{ github.event.pull_request.head.repo.full_name }} | |
| BASE_REPO: ${{ github.event.pull_request.base.repo.full_name }} | |
| CHANGED_FILES: ${{ steps.changed.outputs.files }} | |
| run: bash base/.github/scripts/validate-submission.sh | |
| - name: Comment on PR | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| let body; | |
| try { | |
| body = fs.readFileSync('/tmp/validation-comment.md', 'utf8'); | |
| } catch { | |
| body = '⚠️ Validation script did not produce output.'; | |
| } | |
| const marker = '<!-- lambda-prize-validation -->'; | |
| body = marker + '\n' + body; | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const existing = comments.find(c => (c.body || '').includes(marker)); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body, | |
| }); | |
| } |