Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"permissions": {
"allow": [
"Read(//workspaces/**)",
"Bash(find / -maxdepth 5 -type d \\\\\\( -name \"PandABlocks-FPGA\" -o -name \"PandABlocks-server\" -o -name \"PandABlocks-rootfs\" -o -name \"PandABlocks.github.io\" \\\\\\))"
]
}
}
10 changes: 6 additions & 4 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ FROM ghcr.io/siemens/kas/kas:4.8 AS developer

USER root

# Add any system dependencies for the developer environment here
# RUN apt-get update -y && apt-get install -y --no-install-recommends \
# some-tool \
# && apt-get clean
# Add any system dependencies for the developer environment here.
# npm provides npx, used by `make docs` to run mystmd on demand for the docs build.
RUN apt-get update -y && apt-get install -y --no-install-recommends \
make \
npm \
&& apt-get clean
11 changes: 0 additions & 11 deletions .github/pages/index.html

This file was deleted.

96 changes: 0 additions & 96 deletions .github/pages/make_switcher.py

This file was deleted.

87 changes: 87 additions & 0 deletions .github/workflows/_docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
on:
workflow_call:
outputs:
version-name:
description: The version name this build was served at (pr-<n> | main | docs | <tag>).
value: ${{ jobs.build.outputs.version-name }}

# Build the docs at the versioned BASE_URL and upload this build's `docs` artifact
# (docs.zip, bare html/ root). This is the UNPRIVILEGED half: it runs for every
# event — PRs (including forks), pushes to main/docs, and tags — but never
# publishes. _publish.yml (nested by ci.yml on internal events) reconstructs the
# whole site from these artifacts + release assets and deploys it to Pages.
#
# Like the other PandABlocks repos, the build is driven through `make docs`, which
# runs mystmd on demand via npx (pinned by MYSTMD_VERSION in CONFIG) — so we only
# need Node on the PATH. meta-panda's docs are pure MyST (no python directives).
jobs:
build:
runs-on: ubuntu-latest
outputs:
version-name: ${{ steps.ver.outputs.version-name }}
steps:
- uses: actions/checkout@v5

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

# `make docs` reads MYSTMD_VERSION from CONFIG.
- name: Create CONFIG
run: cp CONFIG.example CONFIG

# Version name = the site sub-dir this build is served at, and the BASE_URL it
# must be built with. pr-<n> for PRs; otherwise the ref name (main, docs, or a
# tag without `/`). No sanitisation: every name is filesystem/URL-safe already.
- name: Compute version name
id: ver
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = pull_request ]; then
name="pr-${{ github.event.pull_request.number }}"
else
name="${{ github.ref_name }}"
fi
echo "version-name=$name" >> "$GITHUB_OUTPUT"

# BASE_URL must match the versioned sub-path the build is served at, or its
# root-absolute assets 404. assemble files this build's artifact at the same
# version name, so the two cannot drift.
- name: Build docs
env:
BASE_URL: /meta-panda/${{ steps.ver.outputs.version-name }}
run: make docs

# Drop the ~135 MB templates/ dir (downloaded book-theme node sources, a
# build-time cache) before packing.
- name: Remove build cache
run: rm -rf docs/_build/templates

# Pack the build as docs.zip with a bare html/ root — the durable contract
# both _publish.yml's gather and the docs-release asset rely on.
- name: Pack docs.zip (bare html/ root)
run: |
set -euo pipefail
( cd docs/_build && zip -rq "$RUNNER_TEMP/docs.zip" html )

# compression-level 0: docs.zip is already compressed.
- name: Upload docs artifact
uses: actions/upload-artifact@v4
with:
name: docs
path: ${{ runner.temp }}/docs.zip
compression-level: 0

# Fork PRs build + verify here but do not auto-publish (ci.yml's publish job
# excludes them — the security boundary). Surface that on the PR.
- name: Explain the fork-preview opt-in
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name != github.repository
run: |
echo "::warning title=Docs preview not published::This is a fork PR, so the \
versioned docs site is NOT auto-published (fork builds run with a read-only \
token). A maintainer can publish a preview by running the Publish workflow \
for PR #${{ github.event.pull_request.number }}: \
https://github.com/${{ github.repository }}/actions/workflows/_publish.yml"
106 changes: 106 additions & 0 deletions .github/workflows/_publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: Publish

# Reconstruct the WHOLE versioned docs site from durable sources (the live branch's
# build, release docs.zip assets, open-PR build artifacts) and deploy it directly to
# GitHub Pages via the published myst-version-switcher `assemble` action — no
# gh-pages branch. This is the PRIVILEGED half, kept in its own file so it can only
# run two trusted ways:
#
# workflow_call — nested by ci.yml AFTER a successful build, for INTERNAL events
# only (internal PRs, pushes to main/docs/tags). ci.yml passes
# this build's `version-name`; assemble downloads this run's
# `docs` artifact and stages it directly (the run isn't a
# completed success yet, so the gather can't discover it).
# workflow_dispatch — a maintainer's opt-in to preview an EXTERNAL fork PR.
#
# guard-default-branch is false during the docs migration: the new MyST docs live on
# the `docs` branch, so the default branch (main) has no build to guard against yet.
on:
workflow_call:
inputs:
version-name:
description: Version name (pr-<n> | main | docs | <tag>) of the in-run build to inject.
required: true
type: string
workflow_dispatch:
inputs:
pr:
description: External fork PR number to approve (pins its head SHA) and preview.
required: false

permissions:
contents: read # checkout + read release assets
actions: read # gh run download (this run's + cross-run docs artifacts)
pages: write # deploy to Pages
id-token: write # deploy-pages OIDC
statuses: write # set the preview-approved status on a fork PR head SHA

concurrency:
group: pages
cancel-in-progress: false

jobs:
publish:
# The canonical-repo guard lives in the caller (ci.yml's publish job), not here.
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0 # tags, for version ordering + prerelease detection

# MIGRATION SHIM: the new docs live on the `docs` branch; `main` has no MyST
# build yet, so the assemble model would drop /main/. Until `docs` merges to
# `main`, overlay the existing gh-pages main/ build into the assemble site dir
# ($RUNNER_TEMP/site, which assemble.sh mkdir -p's but never wipes) so generate
# lists `main` in switcher.json and /main/ keeps serving. Remove this step (and
# delete gh-pages) once main itself is migrated.
- name: Stage legacy main/ from gh-pages (migration shim)
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
if git fetch --quiet origin gh-pages 2>/dev/null \
&& git cat-file -e origin/gh-pages:main/index.html 2>/dev/null; then
mkdir -p "$RUNNER_TEMP/site/main"
git archive origin/gh-pages main | tar -x --strip-components=1 -C "$RUNNER_TEMP/site/main"
echo "Staged legacy main/ from gh-pages"
else
echo "::warning::no gh-pages main/ to stage — skipping"
fi

# workflow_dispatch (fork opt-in): pin THIS commit as approved.
- name: Approve fork PR head SHA
if: github.event_name == 'workflow_dispatch' && inputs.pr != ''
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ inputs.pr }}
run: |
set -euo pipefail
sha=$(gh pr view "$PR" --repo "$REPO" --json headRefOid -q .headRefOid)
gh api --method POST "repos/$REPO/statuses/$sha" \
-f state=success -f context=preview-approved \
-f description="Fork docs preview approved"

# On the nested call, `artifact-version-name` tells assemble to download this
# run's `docs` artifact and stage it as that version (it isn't a completed
# success yet). Empty on workflow_dispatch -> a pure durable gather.
- name: Assemble versioned site
id: site
uses: DiamondLightSource/myst-version-switcher-plugin/assemble@v0.5.0
with:
repo: ${{ github.repository }}
guard-default-branch: false
artifact-version-name: ${{ inputs.version-name }}

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: ${{ steps.site.outputs.dir }}

- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
55 changes: 55 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Docs CI

# Build + verify the docs on every event, then publish on INTERNAL events. The
# build (`_docs.yml`) runs for PRs (including forks), pushes to main/docs, and tags,
# and uploads each build's `docs` artifact. Publishing is nested here (the `publish`
# job -> `_publish.yml`) so its status is visible on the PR/commit, but ONLY for
# internal events on this repo: a fork PR's build runs with a read-only token and
# must never deploy.
#
# `docs` is a publish trigger during the docs migration: the new MyST docs live on
# the `docs` branch (main has none yet), so `docs` is the live version.
on:
pull_request:
push:
branches: [main, docs]
tags: ['*'] # '*' never matches '/'

jobs:
docs:
uses: ./.github/workflows/_docs.yml

# Tag-only: attach this build's docs.zip (bare html/ root) to the GitHub Release
# so `assemble` can reconstruct that released version on future deploys.
docs-release:
needs: [docs]
if: github.ref_type == 'tag'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
name: docs
- env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${{ github.ref_name }}" docs.zip --clobber --repo "${{ github.repository }}"

# Internal events only: an internal PR, or a push to main/docs/tag, has a
# same-repo build we can trust + deploy. Fork PRs (head repo != this repo) are
# excluded — _docs.yml's build job warns them instead.
publish:
needs: [docs]
if: >-
github.repository == 'PandABlocks/meta-panda' &&
( github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository )
uses: ./.github/workflows/_publish.yml
with:
version-name: ${{ needs.docs.outputs.version-name }}
permissions:
contents: read
actions: read
pages: write
id-token: write
statuses: write
Loading
Loading