From 5a6ed10770a5666ffcd15f9d3a7e5827cf40f4c9 Mon Sep 17 00:00:00 2001 From: Lawrence David Date: Tue, 12 May 2026 15:49:24 -0400 Subject: [PATCH] Add PR preview deploys for non-source branches Adds .github/workflows/preview.yml that builds mkdocs and publishes to main/previews// for any non-source branch push or PR against source. Comments the preview URL on PRs (sticky, updated on each push) and removes the subfolder when the PR is closed. README.md documents the preview URL pattern and reminds editors to keep the pip install line in sync across ci.yml and preview.yml. Closes BINF-100 --- .github/workflows/preview.yml | 166 ++++++++++++++++++++++++++++++++++ README.md | 35 +++++++ 2 files changed, 201 insertions(+) create mode 100644 .github/workflows/preview.yml create mode 100644 README.md diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000..5a95629 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,166 @@ +name: preview + +on: + push: + branches-ignore: + - source + - main + - gh-pages + pull_request: + types: [opened, reopened, synchronize, closed] + branches: + - source + +permissions: + contents: write + pull-requests: write + +concurrency: + group: preview-${{ github.head_ref || github.ref_name }} + cancel-in-progress: true + +jobs: + build-preview: + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Resolve branch name + id: branch + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + RAW="${{ github.head_ref }}" + else + RAW="${GITHUB_REF#refs/heads/}" + fi + SLUG=$(echo "$RAW" | tr '/' '-') + echo "raw=$RAW" >> "$GITHUB_OUTPUT" + echo "slug=$SLUG" >> "$GITHUB_OUTPUT" + - name: Configure Git Credentials + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV + - uses: actions/cache@v4 + with: + key: mkdocs-material-${{ env.cache_id }} + path: ~/.cache + restore-keys: | + mkdocs-material- + - run: pip install mkdocs-material mkdocs-git-authors-plugin mkdocs-git-revision-date-localized-plugin + - run: mkdocs build + - name: Deploy preview to main/previews/ + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./site + publish_branch: main + destination_dir: previews/${{ steps.branch.outputs.slug }} + keep_files: true + commit_message: "preview: ${{ steps.branch.outputs.raw }} @ ${{ github.sha }}" + - name: Comment preview URL on PR + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const slug = '${{ steps.branch.outputs.slug }}'; + const raw = '${{ steps.branch.outputs.raw }}'; + const url = `https://lad-lab.github.io/previews/${slug}/`; + const marker = ''; + const sha = context.payload.pull_request.head.sha; + const body = [ + marker, + `Preview build for \`${raw}\`:`, + '', + url, + '', + `Built from commit ${sha}. Allow ~1-2 min for GitHub Pages to refresh.`, + ].join('\n'); + 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 && 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, + }); + } + + cleanup-preview: + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + steps: + - name: Compute slug + id: branch + run: | + RAW="${{ github.head_ref }}" + SLUG=$(echo "$RAW" | tr '/' '-') + echo "raw=$RAW" >> "$GITHUB_OUTPUT" + echo "slug=$SLUG" >> "$GITHUB_OUTPUT" + - name: Checkout main + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 1 + token: ${{ secrets.GITHUB_TOKEN }} + - name: Configure Git Credentials + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + - name: Remove preview folder and push + run: | + if [ -d "previews/${{ steps.branch.outputs.slug }}" ]; then + git rm -rf "previews/${{ steps.branch.outputs.slug }}" + git commit -m "preview: cleanup ${{ steps.branch.outputs.raw }}" + git push origin main + else + echo "No preview folder for slug '${{ steps.branch.outputs.slug }}'; nothing to clean up" + fi + - name: Update PR comment + uses: actions/github-script@v7 + with: + script: | + const raw = '${{ steps.branch.outputs.raw }}'; + const marker = ''; + const body = [ + marker, + `Preview for \`${raw}\` removed.`, + ].join('\n'); + 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 && 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, + }); + } diff --git a/README.md b/README.md new file mode 100644 index 0000000..32142f2 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# lad-lab.github.io + +Source for the David Lab FoodSeq Handbook ([lad-lab.github.io](https://lad-lab.github.io)). Built with [MkDocs](https://www.mkdocs.org/) + Material theme. + +## Branches + +- `source` — markdown source of truth. Edit here. +- `main` — built static site (CI output). Don't edit; GitHub Pages serves this. + +## Local preview + +```bash +pip install mkdocs-material mkdocs-git-authors-plugin mkdocs-git-revision-date-localized-plugin +mkdocs serve +``` + +Open http://127.0.0.1:8000. + +## PR preview deploys + +Pushes to any branch other than `source` / `main` build the site and publish it to a per-branch subfolder of `main`: + +``` +https://lad-lab.github.io/previews// +``` + +Branch names with `/` are flattened with `-` (e.g. `feature/foo` becomes `previews/feature-foo/`). When a PR is opened against `source`, the workflow comments the preview URL on the PR and updates it on every push. Closing or merging the PR deletes the subfolder. + +Workflow: `.github/workflows/preview.yml`. + +## Production deploy + +Push to `source` triggers `.github/workflows/ci.yml`, which runs `mkdocs build` and publishes `./site` to the root of `main`. Previews under `main/previews/` are kept across deploys. + +If you add an mkdocs plugin, update the `pip install` line in **both** `ci.yml` and `preview.yml`.