diff --git a/.github/scripts/publish-pages.sh b/.github/scripts/publish-pages.sh new file mode 100644 index 0000000..1e25013 --- /dev/null +++ b/.github/scripts/publish-pages.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +set -euo pipefail + +mode=${1:?Usage: publish-pages.sh [source-directory] [pr-number]} +source_directory=${2:-} +pr_number=${3:-} +repository_root=$(git rev-parse --show-toplevel) +pages_directory=$(mktemp -d "${RUNNER_TEMP:-/tmp}/rifm-pages.XXXXXX") + +cleanup() { + git -C "$repository_root" worktree remove --force "$pages_directory" >/dev/null 2>&1 || true + rm -rf "$pages_directory" +} +trap cleanup EXIT + +if git ls-remote --exit-code --heads origin gh-pages >/dev/null 2>&1; then + git -C "$repository_root" fetch origin gh-pages:refs/remotes/origin/gh-pages + git -C "$repository_root" worktree add --detach "$pages_directory" origin/gh-pages +else + git -C "$repository_root" worktree add --detach "$pages_directory" HEAD + git -C "$pages_directory" checkout --orphan gh-pages + git -C "$pages_directory" rm -rf . +fi + +case "$mode" in + production) + test -d "$source_directory" + # Replace production while preserving all pr-* preview directories. + find "$pages_directory" -mindepth 1 -maxdepth 1 \ + ! -name .git ! -name .nojekyll ! -name 'pr-*' -exec rm -rf {} + + cp -a "$source_directory"/. "$pages_directory"/ + commit_message="Deploy production site" + ;; + preview) + test -d "$source_directory" + test -n "$pr_number" + rm -rf "$pages_directory/pr-$pr_number" + mkdir -p "$pages_directory/pr-$pr_number" + cp -a "$source_directory"/. "$pages_directory/pr-$pr_number/" + commit_message="Deploy preview for PR #$pr_number" + ;; + delete) + test -n "$pr_number" + rm -rf "$pages_directory/pr-$pr_number" + commit_message="Remove preview for PR #$pr_number" + ;; + *) + echo "Unknown publishing mode: $mode" >&2 + exit 1 + ;; +esac + +touch "$pages_directory/.nojekyll" +git -C "$pages_directory" add -A + +if git -C "$pages_directory" diff --cached --quiet; then + echo "Pages content is already up to date." + exit 0 +fi + +git -C "$pages_directory" config user.name github-actions\[bot\] +git -C "$pages_directory" config user.email 41898282+github-actions\[bot\]@users.noreply.github.com +# This generated branch has no package.json, so repository hooks cannot run here. +git -C "$pages_directory" commit --no-verify -m "$commit_message" +git -C "$pages_directory" push origin HEAD:gh-pages diff --git a/.github/workflows/pages-preview.yml b/.github/workflows/pages-preview.yml new file mode 100644 index 0000000..8a17187 --- /dev/null +++ b/.github/workflows/pages-preview.yml @@ -0,0 +1,86 @@ +name: Deploy PR preview + +on: + pull_request: + types: [opened, synchronize, reopened, closed] + +permissions: + contents: write + pull-requests: write + +# Prevent lost updates when multiple previews or production deploy together. +concurrency: + group: pages-publish + cancel-in-progress: false + +jobs: + deploy: + if: >- + github.event.action != 'closed' && + github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + + - uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 + + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: 22 + cache: pnpm + + - run: pnpm install --frozen-lockfile + - run: pnpm run build + - run: pnpm --dir website run build + env: + BASE_PATH: /rifm/pr-${{ github.event.pull_request.number }}/ + + - name: Publish preview + run: >- + bash .github/scripts/publish-pages.sh preview website/dist + "${{ github.event.pull_request.number }}" + + - name: Add preview link to PR + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + with: + script: | + const marker = ''; + const owner = context.repo.owner.toLowerCase(); + const repo = context.repo.repo; + const url = `https://${owner}.github.io/${repo}/pr-${process.env.PR_NUMBER}/`; + const body = `${marker}\nšŸš€ PR preview: ${url}`; + const comments = await github.paginate(github.rest.issues.listComments, { + ...context.repo, + issue_number: context.issue.number, + }); + const existing = comments.find(comment => + comment.user?.type === 'Bot' && comment.body?.includes(marker) + ); + if (existing) { + await github.rest.issues.updateComment({ + ...context.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body, + }); + } + + cleanup: + if: >- + github.event.action == 'closed' && + github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + + - name: Remove preview + run: >- + bash .github/scripts/publish-pages.sh delete "" + "${{ github.event.pull_request.number }}" diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 5bfeda1..9dd1d31 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -7,21 +7,19 @@ on: workflow_dispatch: permissions: - contents: read + contents: write +# Preview and production jobs both mutate the same gh-pages branch. concurrency: - group: pages + group: pages-publish cancel-in-progress: false jobs: - build: - if: github.ref == 'refs/heads/main' - name: Build + deploy: + name: Deploy runs-on: ubuntu-22.04 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - with: - persist-credentials: false - name: Install pnpm uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 @@ -30,30 +28,11 @@ jobs: cache: pnpm - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Configure Pages - id: pages - uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 - name: Build package run: pnpm run build - name: Build website - run: pnpm --dir website build --base "${{ steps.pages.outputs.base_path }}/" - - name: Upload Pages artifact - uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 - with: - path: website/dist - - deploy: - if: github.ref == 'refs/heads/main' - name: Deploy - permissions: - pages: write - id-token: write - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-22.04 - needs: build - steps: - - name: Deploy to Pages - id: deployment - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 + run: pnpm --dir website run build + env: + BASE_PATH: /rifm/ + - name: Publish production site + run: bash .github/scripts/publish-pages.sh production website/dist diff --git a/website/vite.config.ts b/website/vite.config.ts index 6b79f59..140c6a0 100644 --- a/website/vite.config.ts +++ b/website/vite.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from "vite"; export default defineConfig({ + base: process.env.BASE_PATH || "/", optimizeDeps: { rolldownOptions: { transform: {