diff --git a/.github/scripts/hide-old-deploy-comments.mjs b/.github/scripts/hide-old-deploy-comments.mjs new file mode 100644 index 0000000..b969711 --- /dev/null +++ b/.github/scripts/hide-old-deploy-comments.mjs @@ -0,0 +1,31 @@ +// Minimizes previous deploy-preview comments so only the latest is visible. +// Called from preview-deploy.yml via actions/github-script. + +export default async function run({ github, context, prNumber }) { + const comments = await github.paginate(github.rest.issues.listComments, { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + + await Promise.all( + comments + .filter( + (c) => + c.user.login === "github-actions[bot]" && + c.body.includes("") + ) + .map((c) => + github.graphql( + ` + mutation($id: ID!) { + minimizeComment(input: { subjectId: $id, classifier: OUTDATED }) { + minimizedComment { isMinimized } + } + } + `, + { id: c.node_id } + ) + ) + ); +} diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml deleted file mode 100644 index 64a86b9..0000000 --- a/.github/workflows/pages.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Deploy Pages - -on: - push: - branches: [main] - workflow_dispatch: - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: pages - cancel-in-progress: false - -jobs: - build: - name: Build site - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v5 - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: "24" - cache: "npm" - - - name: Install dependencies - run: npm ci - - - name: Build GAP website - run: npm run build - - - name: Configure Pages - uses: actions/configure-pages@v6 - - - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v5 - with: - path: _site - - deploy: - name: Deploy site - needs: build - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v5 diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml new file mode 100644 index 0000000..06d1239 --- /dev/null +++ b/.github/workflows/preview-build.yml @@ -0,0 +1,41 @@ +name: Build Preview + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +concurrency: + group: preview-build-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + build: + name: Build site + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + + - name: Setup Node.js + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + with: + node-version: "24" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Build GAP website + run: npm run build + + - name: Upload build artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: preview-build + path: _site + include-hidden-files: true + retention-days: 3 diff --git a/.github/workflows/preview-deploy.yml b/.github/workflows/preview-deploy.yml new file mode 100644 index 0000000..5956201 --- /dev/null +++ b/.github/workflows/preview-deploy.yml @@ -0,0 +1,98 @@ +name: Deploy Preview + +on: + workflow_run: + workflows: ["Build Preview"] + types: [completed] + +permissions: + contents: read + actions: read + pull-requests: write + +jobs: + deploy: + name: Deploy preview to Cloudflare + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' }} + + steps: + - name: Checkout wrangler config + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + ref: main + sparse-checkout: | + wrangler.jsonc + .github/scripts + + # Security: actions/download-artifact handles zip extraction safely + # (immune to zip-slip), and the artifact only contains static HTML/CSS/JS + # built by the unprivileged preview-build workflow. + - name: Download build artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: preview-build + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + path: _site + + - name: Get PR number + id: pr + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_NUM=$(gh pr list --repo "${{ github.repository }}" \ + --head "${{ github.event.workflow_run.head_branch }}" \ + --json number --jq '.[0].number') + if ! [[ "$PR_NUM" =~ ^[0-9]+$ ]]; then + echo "::error::Could not determine PR number" + exit 1 + fi + echo "number=$PR_NUM" >> "$GITHUB_OUTPUT" + + # Security: Wrangler is configured for static asset serving only (see + # wrangler.jsonc). The uploaded content is pre-built HTML/CSS/JS with no + # server-side execution capability. + - name: Deploy preview to Cloudflare Workers + id: deploy + uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: versions upload --no-bundle --preview-alias "pr-${{ steps.pr.outputs.number }}" --message "PR #${{ steps.pr.outputs.number }} preview (${{ github.event.workflow_run.head_sha }})" + + - name: Hide old deploy preview comments + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + script: | + const { default: run } = await import('${{ github.workspace }}/.github/scripts/hide-old-deploy-comments.mjs'); + await run({ github, context, prNumber: ${{ steps.pr.outputs.number }} }); + + - name: Comment on PR (success) + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ steps.pr.outputs.number }}, + body: [ + '', + '### 🚀 Deploy Preview', + '', + '- **This commit:** ${{ steps.deploy.outputs.deployment-url }}', + '- **This PR** (kept up to date): https://pr-${{ steps.pr.outputs.number }}-gaps.graphql-foundation.workers.dev' + ].join('\n') + }); + + - name: Comment on PR (failure) + if: failure() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ steps.pr.outputs.number }}, + body: 'Deploy preview failed.\n\n```\ngh run view ${{ github.run_id }} --repo ${{ github.repository }} --log-failed\n```' + }); diff --git a/wrangler.jsonc b/wrangler.jsonc new file mode 100644 index 0000000..7ffd0bb --- /dev/null +++ b/wrangler.jsonc @@ -0,0 +1,8 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "gaps", + "compatibility_date": "2026-05-23", + "assets": { + "directory": "./_site" + } +}