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`.