From 364df9f91fac0a24c9ea963cc8ce0a26704cb66d Mon Sep 17 00:00:00 2001 From: Victor Viale Date: Fri, 31 Jul 2026 09:31:15 +0200 Subject: [PATCH 1/2] Add branch previews of the website with cleanup on merge/close --- .github/workflows/ci.yml | 56 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e6ccd3f..24270569 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,14 +2,20 @@ name: Continuous Integration on: pull_request: - branches: ['**'] + branches: ["**"] + types: [opened, synchronize, reopened, closed] push: - branches: ['main'] + branches: ["main"] + +env: + BRANCH_NAME: pr-${{ github.event.pull_request.number }} + CLOUDFLARE_PAGES_PROJECT_NAME: typelevel-website jobs: build: name: Build and Test runs-on: ubuntu-latest + if: github.event.action != 'closed' steps: - uses: actions/checkout@v6 - uses: actions/setup-java@v5 @@ -22,9 +28,55 @@ jobs: scala-cli-version: 1.12.2 - run: scala-cli fmt --check . - run: scala-cli --server=false build.scala + - name: Publish to Cloudflare Pages + if: github.event_name == 'pull_request' && github.event.pull_request.merged != true + uses: cloudflare/wrangler-action@v4 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + gitHubToken: ${{ secrets.GITHUB_TOKEN }} + command: pages deploy ./target --project-name=${{ env.CLOUDFLARE_PAGES_PROJECT_NAME }} --branch=${{ env.BRANCH_NAME }} - if: github.event_name != 'pull_request' uses: peaceiris/actions-gh-pages@v4.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: target cname: typelevel.org + + cleanup-preview: + name: Delete Cloudflare Pages preview deployments + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' && github.event.action == 'closed' + steps: + - name: Delete deployments for this PR's branch + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: | + set -euo pipefail + + API="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/pages/projects/$CLOUDFLARE_PAGES_PROJECT_NAME/deployments" + AUTH_HEADER="Authorization: Bearer $CLOUDFLARE_API_TOKEN" + + page=1 + ids=() + while :; do + response=$(curl -sf -H "$AUTH_HEADER" "$API?page=$page&per_page=25") + count=$(echo "$response" | jq '.result | length') + [ "$count" -eq 0 ] && break + while IFS= read -r id; do + ids+=("$id") + done < <(echo "$response" | jq -r --arg branch "$BRANCH_NAME" \ + '.result[] | select(.deployment_trigger.metadata.branch == $branch) | .id') + page=$((page + 1)) + done + + if [ ${#ids[@]} -eq 0 ]; then + echo "No preview deployments found for branch $BRANCH_NAME" + exit 0 + fi + + for id in "${ids[@]}"; do + echo "Deleting deployment $id" + curl -sf -X DELETE -H "$AUTH_HEADER" "$API/$id?force=true" + done From 1ea1c41d89d4beda200f0682f1a984eb6cae6bc2 Mon Sep 17 00:00:00 2001 From: Victor Viale Date: Mon, 3 Aug 2026 15:46:48 +0200 Subject: [PATCH 2/2] Switch to a privileged workflow setup to avoid extracting secrets --- .github/workflows/ci.yml | 72 +++++++----------------- .github/workflows/pr-preview-cleanup.yml | 50 ++++++++++++++++ .github/workflows/pr-preview-deploy.yml | 46 +++++++++++++++ 3 files changed, 116 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/pr-preview-cleanup.yml create mode 100644 .github/workflows/pr-preview-deploy.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24270569..9c59b9e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,19 +3,13 @@ name: Continuous Integration on: pull_request: branches: ["**"] - types: [opened, synchronize, reopened, closed] push: branches: ["main"] -env: - BRANCH_NAME: pr-${{ github.event.pull_request.number }} - CLOUDFLARE_PAGES_PROJECT_NAME: typelevel-website - jobs: build: name: Build and Test runs-on: ubuntu-latest - if: github.event.action != 'closed' steps: - uses: actions/checkout@v6 - uses: actions/setup-java@v5 @@ -28,55 +22,29 @@ jobs: scala-cli-version: 1.12.2 - run: scala-cli fmt --check . - run: scala-cli --server=false build.scala - - name: Publish to Cloudflare Pages - if: github.event_name == 'pull_request' && github.event.pull_request.merged != true - uses: cloudflare/wrangler-action@v4 + - name: Save PR number + if: github.event_name == 'pull_request' + run: echo "${{ github.event.pull_request.number }}" > pr_number.txt + - name: Upload site artifact + if: github.event_name == 'pull_request' + uses: actions/upload-artifact@v4 + with: + name: site + path: target + if-no-files-found: error + retention-days: 30 + - name: Upload PR number artifact + if: github.event_name == 'pull_request' + uses: actions/upload-artifact@v4 with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - gitHubToken: ${{ secrets.GITHUB_TOKEN }} - command: pages deploy ./target --project-name=${{ env.CLOUDFLARE_PAGES_PROJECT_NAME }} --branch=${{ env.BRANCH_NAME }} - - if: github.event_name != 'pull_request' + name: pr-number + path: pr_number.txt + if-no-files-found: error + retention-days: 30 + - name: Publish to GitHub Pages + if: github.event_name == 'push' uses: peaceiris/actions-gh-pages@v4.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: target cname: typelevel.org - - cleanup-preview: - name: Delete Cloudflare Pages preview deployments - runs-on: ubuntu-latest - if: github.event_name == 'pull_request' && github.event.action == 'closed' - steps: - - name: Delete deployments for this PR's branch - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - run: | - set -euo pipefail - - API="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/pages/projects/$CLOUDFLARE_PAGES_PROJECT_NAME/deployments" - AUTH_HEADER="Authorization: Bearer $CLOUDFLARE_API_TOKEN" - - page=1 - ids=() - while :; do - response=$(curl -sf -H "$AUTH_HEADER" "$API?page=$page&per_page=25") - count=$(echo "$response" | jq '.result | length') - [ "$count" -eq 0 ] && break - while IFS= read -r id; do - ids+=("$id") - done < <(echo "$response" | jq -r --arg branch "$BRANCH_NAME" \ - '.result[] | select(.deployment_trigger.metadata.branch == $branch) | .id') - page=$((page + 1)) - done - - if [ ${#ids[@]} -eq 0 ]; then - echo "No preview deployments found for branch $BRANCH_NAME" - exit 0 - fi - - for id in "${ids[@]}"; do - echo "Deleting deployment $id" - curl -sf -X DELETE -H "$AUTH_HEADER" "$API/$id?force=true" - done diff --git a/.github/workflows/pr-preview-cleanup.yml b/.github/workflows/pr-preview-cleanup.yml new file mode 100644 index 00000000..9456bd5b --- /dev/null +++ b/.github/workflows/pr-preview-cleanup.yml @@ -0,0 +1,50 @@ +name: Cleanup PR Preview + +on: + pull_request_target: + types: [closed] + +permissions: + contents: read + +env: + CLOUDFLARE_PAGES_PROJECT_NAME: typelevel-website + BRANCH_NAME: pr-${{ github.event.pull_request.number }} + +jobs: + cleanup-preview: + name: Delete Cloudflare Pages preview deployments + runs-on: ubuntu-latest + steps: + - name: Delete deployments for this PR's branch + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: | + set -euo pipefail + + API="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/pages/projects/$CLOUDFLARE_PAGES_PROJECT_NAME/deployments" + AUTH_HEADER="Authorization: Bearer $CLOUDFLARE_API_TOKEN" + + page=1 + ids=() + while :; do + response=$(curl -sf -H "$AUTH_HEADER" "$API?page=$page&per_page=25") + count=$(echo "$response" | jq '.result | length') + [ "$count" -eq 0 ] && break + while IFS= read -r id; do + ids+=("$id") + done < <(echo "$response" | jq -r --arg branch "$BRANCH_NAME" \ + '.result[] | select(.deployment_trigger.metadata.branch == $branch) | .id') + page=$((page + 1)) + done + + if [ ${#ids[@]} -eq 0 ]; then + echo "No preview deployments found for branch $BRANCH_NAME" + exit 0 + fi + + for id in "${ids[@]}"; do + echo "Deleting deployment $id" + curl -sf -X DELETE -H "$AUTH_HEADER" "$API/$id?force=true" + done diff --git a/.github/workflows/pr-preview-deploy.yml b/.github/workflows/pr-preview-deploy.yml new file mode 100644 index 00000000..c4fb6e6c --- /dev/null +++ b/.github/workflows/pr-preview-deploy.yml @@ -0,0 +1,46 @@ +name: Deploy PR Preview + +on: + workflow_run: + workflows: ["Continuous Integration"] + types: [completed] + +permissions: + actions: read + contents: read + +env: + CLOUDFLARE_PAGES_PROJECT_NAME: typelevel-website + +jobs: + deploy: + name: Publish to Cloudflare Pages + runs-on: ubuntu-latest + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + steps: + - name: Download site artifact + uses: actions/download-artifact@v4 + with: + name: site + path: site + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Download PR number artifact + uses: actions/download-artifact@v4 + with: + name: pr-number + path: . + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Read PR number + id: pr + run: echo "branch=pr-$(cat pr_number.txt)" >> "$GITHUB_OUTPUT" + - name: Deploy + uses: cloudflare/wrangler-action@v4 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + gitHubToken: ${{ secrets.GITHUB_TOKEN }} + command: pages deploy ./site --project-name=${{ env.CLOUDFLARE_PAGES_PROJECT_NAME }} --branch=${{ steps.pr.outputs.branch }}