Add vibe coded rollercoaster with loop #21
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
| # Publishes each PR's viz_submission/ to the workshop gallery. | |
| # | |
| # Two jobs, on purpose: | |
| # prepare - checks out the PR, assembles the page, and screenshots it in a | |
| # headless browser. This runs untrusted PR content, so it | |
| # references NO secrets. | |
| # publish - has the secrets, but only touches the artifact produced by | |
| # `prepare` (jq + curl). It must never execute PR code. | |
| # | |
| # pull_request_target is used so PRs from forks can reach the repo secrets. | |
| # | |
| # Required repo secrets: | |
| # GALLERY_API_URL e.g. https://scoreboard-production-df34.up.railway.app (no trailing slash) | |
| # GALLERY_CI_KEY the key configured on the gallery server | |
| name: Publish to gallery | |
| on: | |
| pull_request_target: | |
| types: [opened, synchronize, reopened, edited] | |
| permissions: | |
| contents: read | |
| jobs: | |
| prepare: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out the PR's files | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ github.event.pull_request.head.repo.full_name }} | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| # Safe to opt in here: this job references no secrets, and | |
| # persist-credentials:false keeps the GITHUB_TOKEN out of .git/config | |
| # so the PR's page JS (run for the screenshot) can't read it. The | |
| # publish job holds the secrets and never executes PR code. | |
| allow-unsafe-pr-checkout: true | |
| persist-credentials: false | |
| - name: Assemble the page | |
| env: | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p out | |
| if [ -f viz_submission/index.html ]; then | |
| cp viz_submission/index.html out/page.html | |
| else | |
| # No index.html: wrap the submitted image(s) in a minimal page. | |
| # Images are referenced by commit-pinned GitHub raw URLs, so the | |
| # gallery only ever hosts one small HTML file per participant. | |
| IMGS=() | |
| while IFS= read -r f; do IMGS+=("$f"); done < <( | |
| find viz_submission -maxdepth 1 -type f \ | |
| \( -iname '*.png' -o -iname '*.gif' -o -iname '*.jpg' \ | |
| -o -iname '*.jpeg' -o -iname '*.svg' -o -iname '*.webp' \) | sort) | |
| if [ "${#IMGS[@]}" -eq 0 ]; then | |
| echo "::error::no submission found - add viz_submission/index.html or an image (.png/.gif/.jpg/.svg/.webp)" | |
| exit 1 | |
| fi | |
| { | |
| printf '<!doctype html><html><head><meta charset="utf-8"><title>%s</title>\n' \ | |
| "$(jq -rn --arg t "$PR_TITLE" '$t|@html')" | |
| printf '<style>body{margin:0;background:#f3f2f7;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:24px;padding:24px;box-sizing:border-box}img{max-width:min(1100px,94vw);max-height:86vh;border-radius:12px;box-shadow:0 10px 34px rgba(20,15,40,.15)}</style></head><body>\n' | |
| for f in "${IMGS[@]}"; do | |
| printf '<img src="https://raw.githubusercontent.com/%s/%s/viz_submission/%s" alt="">\n' \ | |
| "$HEAD_REPO" "$HEAD_SHA" "$(jq -rn --arg s "$(basename "$f")" '$s|@uri')" | |
| done | |
| printf '</body></html>\n' | |
| } > out/page.html | |
| fi | |
| - name: Screenshot for the card thumbnail (best effort) | |
| continue-on-error: true | |
| run: | | |
| set -euo pipefail | |
| npx --yes [email protected] install --with-deps chromium | |
| npx --yes [email protected] screenshot --browser chromium \ | |
| --viewport-size "1280,800" --wait-for-timeout 5000 \ | |
| "file://$PWD/out/page.html" out/thumb.png | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: submission | |
| path: out/ | |
| retention-days: 1 | |
| publish: | |
| needs: prepare | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: submission | |
| path: out/ | |
| - name: Publish to the gallery | |
| env: | |
| API_URL: ${{ secrets.GALLERY_API_URL }} | |
| CI_KEY: ${{ secrets.GALLERY_CI_KEY }} | |
| PR_AUTHOR: ${{ github.event.pull_request.user.login }} | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| PR_BODY: ${{ github.event.pull_request.body }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${API_URL:-}" ] || [ -z "${CI_KEY:-}" ]; then | |
| echo "::error::GALLERY_API_URL / GALLERY_CI_KEY repo secrets are not set" | |
| exit 1 | |
| fi | |
| THUMB_ARGS=() | |
| if [ -s out/thumb.png ]; then | |
| base64 -w0 out/thumb.png > out/thumb.b64 | |
| THUMB_ARGS=(--rawfile thumb out/thumb.b64) | |
| fi | |
| jq -n --arg name "$PR_AUTHOR" --arg title "$PR_TITLE" --arg desc "$PR_BODY" \ | |
| --rawfile html out/page.html "${THUMB_ARGS[@]}" \ | |
| '{name:$name, title:$title, description:$desc, html:$html} | |
| + (if $ARGS.named.thumb then {thumb:$ARGS.named.thumb} else {} end)' | | |
| curl -fsS -X POST "$API_URL/api/append" \ | |
| -H "x-ci-key: $CI_KEY" -H "Content-Type: application/json" -d @- | |
| echo "Published $PR_AUTHOR's submission to the gallery." |