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
58 changes: 58 additions & 0 deletions .github/workflows/cleanup-stale-branches.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Clean up stale branches

on:
push:
branches: [master]
paths:
- .github/workflows/cleanup-stale-branches.yml
workflow_dispatch:

permissions:
contents: write

jobs:
cleanup:
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- name: Delete obsolete branches
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
shell: bash
run: |
set -euo pipefail

branches=(
"agent/final-repository-cleanup"
"agent/fix-vertical-stack-backgrounds"
"agent/pure-deterministic-install"
"agent/stage1-two-repo-runtime"
"agent/stage2-legacy-compatibility"
"agent/stage3-migrate-all-presentations"
"agent/stage4-pure-exporter"
"agent/stage5-pages-integration"
"agent/stage6-remove-reveal-fork"
"agent/stage-7-canonical-sources"
"agent/stage-7-canonical-sources-final"
"chore/vercel-preview-deployments"
"experiment/reveal-6-migration"
"feat/presentation-index"
"fix/reveal-upstream-bug-backports"
"fix/vercel-preview-export-recursion"
"security/reveal-xss-backports"
"docs/production-build-readme"
)

for branch in "${branches[@]}"; do
if gh api "repos/${REPOSITORY}/git/ref/heads/${branch}" >/dev/null 2>&1; then
echo "Deleting ${branch}"
gh api --method DELETE "repos/${REPOSITORY}/git/refs/heads/${branch}"
else
echo "Already absent: ${branch}"
fi
done

echo "Remaining branches:"
gh api --paginate "repos/${REPOSITORY}/branches?per_page=100" --jq '.[].name'
241 changes: 186 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,51 @@
# Digiguru presentations

This repository contains Digiguru's presentation content and the **Pure** Reveal.js runtime. Pure is the only runtime/build path.
Source, runtime and build tooling for the Digiguru presentation archive.

Stage 6 removed the inherited framework/build stack. Stage 7 migrated the then-current 25 presentation sources to the canonical `pure-v1` content format and removed the historical HTML compatibility parser and frozen metadata registry. The rescued Christmas LEGO Movie Sets quiz brings the current canonical corpus to 26 decks.
**Production:** https://art.digiguru.co.uk/presentation/

## Architecture
This repository owns the presentation content and the shared **Pure** Reveal.js runtime. It does **not** directly host the production `/presentation/` site. Production is assembled and deployed by [`digiguru/digiguru.github.io`](https://github.com/digiguru/digiguru.github.io), which checks out an exact commit from this repository and exports the presentation build into its GitHub Pages artifact.

- Root `*.html` files are canonical `pure-v1` presentation sources.
- `pure/src/` contains the shared runtime and UI.
- `pure/src/presentation-runtime/stack-backgrounds.js` preserves shared backgrounds declared on vertical stacks while respecting explicit child backgrounds.
- `pure/build/deck-source.mjs` reads canonical content/configuration.
- `pure/build/audit-sources.mjs` rejects historical runtime wiring and checks the corpus capability/theme footprint.
- `pure/deck.html` is the shared HTML shell for every built deck.
- `scripts/presentations.mjs` discovers sources, validates metadata and exports the built artifact.
- `scripts/presentation-accessibility.mjs` validates accessibility.
## What lives here

- Root `*.html` files are the canonical `pure-v1` presentation sources.
- `pure/src/` contains the shared Reveal.js runtime, UI and presentation behaviour.
- `pure/build/deck-source.mjs` reads and validates canonical presentation source.
- `pure/deck.html` is the shared HTML shell used by every built deck.
- `scripts/presentations.mjs` discovers presentations, validates metadata and exports the built site.
- `scripts/presentation-accessibility.mjs` validates presentation accessibility.
- `scripts/smoke-presentations.mjs` browser-tests the exported website artifact.
- `pure/package.json` and `pure/package-lock.json` define the npm dependency graph.
- `pure/dist/` is generated output; never edit it manually.
- `pure/package.json` and `pure/package-lock.json` define the runtime/build dependency graph.
- `pure/dist/` is generated output. Never edit it manually.

Presentation sources contain content and declarative configuration, not their own runtime.
Presentation source files contain content and declarative configuration; they do not own their own Reveal runtime.

## Setup and validation
## Local development

Use the Node version pinned by `.node-version`.
Use the Node version pinned by `.node-version` (the repository currently requires Node `>=24.11.0 <25`).

Start the development server with:
Start the development server:

```bash
npm start
```

`npm start` performs a deterministic `npm ci --prefix pure` from the committed lockfile before starting Vite.

For a fresh production build:
For a clean production build:

```bash
npm run pure:install
npm run build
```

Useful checks:
The build output is written to:

```text
pure/dist/
```

Useful validation commands:

```bash
npm test
Expand All @@ -51,11 +57,160 @@ npm run pure:smoke
npm run presentations:smoke
```

`npm run build` produces the static product in `pure/dist/` and assumes the locked Pure dependencies are already installed. `npm test`, `npm start` and `npm run pure:check` are self-contained and install them when needed.
`npm run build` assumes the locked Pure dependencies are already installed. `npm test`, `npm start` and `npm run pure:check` install them when needed.

## How production deployment works

Production is deliberately a **two-repository build**.

```text
presentation/master
│ push
GitHub Actions: digiguru/presentation/.github/workflows/js.yml
├─ Pure validation / audit / browser smoke
├─ tooling + metadata + accessibility + lint
├─ npm run build
└─ dispatch-website
│ workflow_dispatch with exact presentation SHA
digiguru/digiguru.github.io/.github/workflows/jekyll.yml
├─ checkout digiguru.github.io
├─ checkout digiguru/presentation at that exact SHA
├─ generate presentation metadata
├─ build Jekyll site
├─ export Pure into _site/presentation
├─ stamp release metadata
├─ validate generated site/assets
├─ deploy GitHub Pages
└─ smoke-test deployed site
https://art.digiguru.co.uk/presentation/
```

### 1. Presentation CI

On pull requests and pushes to `master`, `.github/workflows/js.yml` runs the presentation checks and production build.

The build job installs the locked Pure dependency graph once, then validates workflow syntax, custom tooling, presentation metadata, accessibility and lint before running `npm run build` and the exported-site Chrome smoke tests.

A separate `pure` job runs the Pure checks, dependency audit and direct Pure Chrome smoke tests.

### 2. Cross-repository dispatch

After a push to `master`, `dispatch-website` calls the `digiguru/digiguru.github.io` `jekyll.yml` workflow using the `WEBSITE_DISPATCH_TOKEN` secret.

The dispatch passes the **full 40-character presentation commit SHA** that triggered the build. This is important: the website does not need to guess which presentation revision should be deployed.

### 3. Website build

The website workflow checks out this repository at the supplied SHA under `presentation-source/`, generates the presentation manifest, builds the main Jekyll site, then runs:

```bash
node presentation-source/scripts/presentations.mjs --export _site/presentation
```

That places the Pure output under the website's `/presentation/` path. Release metadata records both the website SHA and the presentation SHA so a deployed build can be traced back to both repositories.

### 4. GitHub Pages deploy

The completed website artifact is deployed by `actions/deploy-pages`. The website repo then runs a deployed-site smoke test.

The canonical public URL is:

**https://art.digiguru.co.uk/presentation/**

## Vercel previews

Vercel is useful for previews, but it is **not the production hosting path** for `art.digiguru.co.uk/presentation/`.

`vercel.json` uses the same deterministic Pure build:

```text
install: npm run pure:install
build: npm run build
output: pure/dist
```

Vercel therefore previews this repository directly, while production embeds the exported build inside the `digiguru.github.io` Pages artifact.

## Key watch-outs

### Do not enable GitHub Pages on this repository

`digiguru/presentation` must not independently claim the production presentation path. Production Pages belongs to `digiguru/digiguru.github.io`.

This previously caused routing conflicts where the old presentation Pages site intercepted `art.digiguru.co.uk/presentation/` instead of the website's exported Pure artifact.

### The website dispatch secret is production infrastructure

`WEBSITE_DISPATCH_TOKEN` must remain configured in this repository and must be allowed to dispatch the website's `jekyll.yml` workflow. If that secret is missing, expired or loses permission, presentation CI can build successfully but the production website will not be asked to rebuild.

### The dispatch currently waits for `build`, not the separate `pure` job

In `.github/workflows/js.yml`, `dispatch-website` currently has:

```yaml
needs: build
```

It does **not** currently depend on the separate `pure` job. A failing `pure` job therefore does not, by itself, prevent the website dispatch if `build` succeeds. If the intention is "every CI job must be green before production dispatch", change the dependency to include both jobs rather than assuming that is already enforced.

### Website pushes can also consume `presentation/master`

The website's `jekyll.yml` also runs for pushes to `digiguru.github.io/main`. When no `presentation_sha` input is supplied, it deliberately checks out `presentation/master`. This is useful for normal website releases, but it means the website repository is also capable of deploying the current presentation `master` independently of this repository's dispatch.

### Keep source and runtime responsibilities separate

Root presentation files are canonical `pure-v1` content. Shared behaviour belongs in `pure/`.

Do not reintroduce:

- deck-owned Reveal runtime bundles
- local Reveal/plugin runtime links
- executable deck-owned Reveal initialization
- a second presentation registry
- hand-edited files in `pure/dist/`

`npm --prefix pure run audit:sources` guards against the historical runtime patterns that the Pure migration removed.

### Preserve presentation metadata

Every presentation must declare at least:

```html
<meta name="presentation-format" content="pure-v1">
<meta name="presentation-name" content="My talk">
<meta name="presentation-version" content="v1.0">
<meta name="presentation-date" content="14/08/2026">
```

`presentation-attendance` is optional. Multiple `presentation-theme` tags are allowed.

### Keep accessibility and background behaviour covered

Every image requires an `alt` attribute.

Backgrounds should remain declarative:

```html
<section data-background-image="assets/example.png" data-background-size="1696px 928px">
```

For vertical stacks, an outer-section background is inherited by children that do not explicitly declare their own background; explicit child backgrounds win. Unit and browser regression tests cover this behaviour.

### Dependency changes must update the Pure lockfile

Runtime/build npm dependencies live in `pure/package.json`. Any dependency change must update `pure/package-lock.json`, and CI/Vercel install with `npm ci --prefix pure`.

## Canonical presentation source format

Every presentation declares metadata and content explicitly:
A minimal source looks like this:

```html
<!doctype html>
Expand All @@ -79,52 +234,28 @@ Every presentation declares metadata and content explicitly:
</div>
```

`presentation-name`, `presentation-version` and `presentation-date` are required. `presentation-attendance` is optional. Multiple `presentation-theme` tags are allowed.

Sources may contain inline `<style>` blocks and explicitly required external HTTP(S) styles/scripts. Reveal configuration belongs in the JSON `presentation-options` block.

Do not add document wrappers, a deck-owned `.reveal` wrapper, local Reveal/plugin runtime links, or executable deck-owned initialization code. `npm --prefix pure run audit:sources` guards against those historical patterns.

Every image needs an `alt` attribute. Keep backgrounds declarative:

```html
<section data-background-image="assets/example.png" data-background-size="1696px 928px">
```

A background declared on the outer section of a vertical stack is inherited by children that do not declare their own background. Explicit child backgrounds win. Unit tests cover this rule and the Chrome corpus smoke verifies the rendered BigBus opening background so this regression cannot silently return.
Do not add document wrappers, a deck-owned `.reveal` wrapper, local Reveal/plugin runtime links or executable deck-owned initialization code.

Shared runtime behaviour belongs in `pure/`; presentation-specific content belongs in the root source file.
## Export contract

## Build and export

The Pure build discovers canonical sources, loads them through `deck-source.mjs`, renders every deck through the shared Pure shell, preserves required assets, and emits `build-info.json` plus `presentations.json`.

The website-facing exporter remains:
The website-facing exporter is:

```bash
node scripts/presentations.mjs --manifest /path/to/presentations.yml --export /path/to/site
```

The generated YAML is an export artifact only; there is no repository-owned presentation registry. Metadata lives in each source file.

## CI and deployment
The generated YAML is an export artifact only. There is no repository-owned presentation registry; metadata lives in each canonical source file.

GitHub Actions validates workflow syntax, repository tooling, all 26 canonical metadata records, accessibility, ESLint, the locked dependency graph, source purity/capability preservation, the production build, all 26 built decks in Chrome, and the exported catalogue plus all 26 exported decks in Chrome.

CI installs the locked Pure dependency graph once per job and reuses it for lint/build/smoke work. After a successful push to `master`, the workflow dispatches `digiguru/digiguru.github.io` with the exact presentation SHA that passed CI.
## Dependencies and security

Vercel uses the same deterministic path from `vercel.json`:
Use:

```text
install: npm run pure:install
build: npm run build
output: pure/dist
```bash
npm run pure:audit
```

The Vercel install step runs `npm ci --prefix pure` once; the build step does not reinstall dependencies.

## Dependencies and security

Runtime/build npm dependencies belong in `pure/package.json`; dependency changes must update `pure/package-lock.json`. `npm run pure:audit` audits the dependency graph that actually builds and runs Pure.
to audit the dependency graph that actually builds and runs Pure.

There is no inherited framework dependency tree and no presentation compatibility runtime to maintain.
The old inherited Reveal.js framework/build tree and historical compatibility parser have been removed. The repository now maintains one presentation source format and one shared runtime.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"bugs": {
"url": "https://github.com/digiguru/presentation/issues"
},
"homepage": "https://art.digiguru.co.uk/presentation/",
"engines": {
"node": ">=24.11.0 <25"
},
Expand Down