diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
new file mode 100644
index 0000000..fa6bef6
--- /dev/null
+++ b/.github/workflows/pr.yml
@@ -0,0 +1,9 @@
+name: PR
+
+on:
+ pull_request:
+ branches: [main]
+
+jobs:
+ build:
+ uses: purview-dev/build/.github/workflows/purview-build.yml@main
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..f156a2b
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,19 @@
+name: Release
+
+on:
+ push:
+ branches: [main]
+
+# Serialize releases; the reusable workflow skips when the v{version} tag already exists.
+concurrency:
+ group: release-${{ github.ref }}
+ cancel-in-progress: false
+
+jobs:
+ release:
+ uses: purview-dev/build/.github/workflows/purview-release.yml@main
+ with:
+ # The site publishes no NuGet package; tag + GitHub release (with the dist zip attached) only.
+ release-mode: GitHubRelease
+ upload-artifacts: true
+ secrets: inherit
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
deleted file mode 100644
index 896ec45..0000000
--- a/.github/workflows/validate.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-name: Validate
-
-on:
- pull_request:
- branches: [main]
- push:
- branches: [main]
- workflow_dispatch:
-
-permissions:
- contents: read
-
-jobs:
- validate:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- # actions/checkout v7.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
-
- - name: Install Bun
- # oven-sh/setup-bun v2.2.0
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
- with:
- bun-version: 1.4.2
-
- - name: Install Just
- # taiki-e/install-action v2.87.13
- uses: taiki-e/install-action@26e9283f268b880168bdbd2c545dfcd60ec2c6ab
- with:
- tool: just@1.58.0
-
- # The repository is a Bun workspace: the lockfile and root `package.json`
- # live here, the Astro site package lives in `src/`, and every `bun run`
- # / `just` recipe delegates to `src/` (see `package.json` scripts).
- - name: Install dependencies
- run: bun install --frozen-lockfile
-
- - name: Run validation
- # `just validate` runs a live data sync on fresh checkouts (no cache /
- # docs mirror yet), so pass a token to avoid unauthenticated rate limits.
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: just validate
diff --git a/README.md b/README.md
index cce56ba..0225eee 100644
--- a/README.md
+++ b/README.md
@@ -119,6 +119,7 @@ output tests.
| `test` | Deterministic unit test suite |
| `test-dist` | Assert built outputs (llms, sitemap, no secrets) |
| `build` | Production build (runs data sync first) |
+| `ci-build` | Full CI validation chain used by the shared pipeline |
| `preview` | Preview the production build |
| `check-assets` | Validate branding sources and generated assets |
| `check-links` | Validate internal links across `dist/` |
@@ -255,6 +256,25 @@ The build generates `/llms.txt`, `/llms-small.txt`, and `/llms-full.txt`
the aggregated documentation. The built outputs are asserted by `tests/dist/`
and `just check-generated`.
+## Site versioning and releases
+
+The site is versioned like every other purview-dev repository. The single
+source of truth is the `version` field in the root `package.json`; the footer
+shows the current `v{version}` (linking to the repository's GitHub releases
+page), read at build time by `src/src/lib/site-version.ts`.
+
+Building and release flow run through the shared
+[Purview.Build](https://purview.dev/projects/build/) system as a `Web` project
+(`Build:ProjectType=Web` in `purview-build.json`): the pipeline runs the root
+`package.json` scripts (`bun install`, `bun run format:check`/`bun run lint`,
+`bun run test`) and the `ci:build` script, which chains data sync, typecheck,
+asset checks, the production build, link crawl, generated-output checks, and
+built-output tests. Releasing means bumping `version` in the root
+`package.json` and merging: the `Release` workflow (`purview-release.yml`,
+`release-mode: GitHubRelease`) runs the pipeline, tags `v{version}`, and uploads
+the built `dist/` zip as a GitHub release asset. `deploy.yml` continues to
+publish the site to GitHub Pages.
+
## GitHub Pages deployment
This repository is the organisation Pages repository
diff --git a/justfile b/justfile
index 6d42536..7943429 100644
--- a/justfile
+++ b/justfile
@@ -64,6 +64,10 @@ build:
bun run data:sync
bun run build
+# Full CI validation chain used by the shared build pipeline (data sync → typecheck → build → checks).
+ci-build:
+ bun run ci:build
+
# Preview the production build locally.
preview:
bun run preview
diff --git a/package.json b/package.json
index 042c55d..76a7801 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,6 @@
{
"name": "purview-dev",
+ "version": "0.1.0",
"private": true,
"description": "Purview-Dev public website and unified documentation portal (workspace root).",
"license": "MIT",
@@ -12,6 +13,7 @@
"check:assets": "bun run --cwd src check:assets",
"check:generated": "bun run --cwd src check:generated",
"check:links": "bun run --cwd src check:links",
+ "ci:build": "bun run data:sync && bun run typecheck && bun run check:assets && bun run build && bun run check:links && bun run check:generated && bun run test:dist",
"clean": "bun run --cwd src clean",
"data:sync": "bun run --cwd src data:sync",
"dev": "bun run --cwd src dev",
diff --git a/purview-build.json b/purview-build.json
new file mode 100644
index 0000000..8945a31
--- /dev/null
+++ b/purview-build.json
@@ -0,0 +1,9 @@
+{
+ "Build": {
+ "ProjectType": "Web",
+ "WebBuildCommand": "bun run ci:build"
+ },
+ "Release": {
+ "Mode": "None"
+ }
+}
\ No newline at end of file
diff --git a/src/src/components/SiteFooter.astro b/src/src/components/SiteFooter.astro
index d6ccbec..7c0f907 100644
--- a/src/src/components/SiteFooter.astro
+++ b/src/src/components/SiteFooter.astro
@@ -2,7 +2,8 @@
import SiteBrand from '~/components/SiteBrand.astro';
import { NAV_ITEMS } from '~/components/SiteNav.astro';
import { loadProjects } from '~/lib/manifest/load';
-import { SITE } from '~/lib/site';
+import { siteVersion } from '~/lib/site-version';
+import { OWNER, SITE } from '~/lib/site';
import { withBase } from '~/lib/urls';
// Shuffled at build time so the footer doesn't always spotlight the same projects.
@@ -10,6 +11,9 @@ const footerProjects = loadProjects()
.filter((project) => project.status !== 'archived')
.toSorted(() => Math.random() - 0.5)
.slice(0, 3);
+
+const version = siteVersion();
+const releasesUrl = `${SITE.githubUrl}/${OWNER}.github.io/releases`;
---