Skip to content

Commit 4669c63

Browse files
proofstone.dev v1: umbrella site (Eleventy) rendering roadmap repos
0 parents  commit 4669c63

24 files changed

Lines changed: 2710 additions & 0 deletions

‎.github/workflows/build-deploy.yml‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Build proofstone.dev and deploy to GitHub Pages.
2+
# Rebuilds on: own push · a roadmap repo signalling new content (repository_dispatch)
3+
# · nightly safety-net (schedule) · manual (workflow_dispatch).
4+
# Action versions verified against GitHub Docs 2026-07-18.
5+
name: build-deploy
6+
7+
on:
8+
push:
9+
branches: [main]
10+
repository_dispatch:
11+
types: [roadmap-content] # a roadmap repo fired this after a README/assets change
12+
schedule:
13+
- cron: '17 4 * * *' # 04:17 UTC nightly, in case a dispatch was missed
14+
workflow_dispatch:
15+
16+
permissions:
17+
contents: read
18+
pages: write
19+
id-token: write
20+
21+
concurrency:
22+
group: pages
23+
cancel-in-progress: true
24+
25+
env:
26+
# noindex stays ON until launch. Flip by setting repo variable SITE_NOINDEX=false.
27+
SITE_NOINDEX: ${{ vars.SITE_NOINDEX || 'true' }}
28+
29+
jobs:
30+
build:
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v4
34+
- uses: actions/setup-node@v4
35+
with:
36+
node-version: 22
37+
cache: npm
38+
- run: npm ci
39+
- run: npm run build # fetch roadmap content, then Eleventy build
40+
- uses: actions/configure-pages@v5
41+
- uses: actions/upload-pages-artifact@v4
42+
with:
43+
path: _site
44+
45+
deploy:
46+
needs: build
47+
runs-on: ubuntu-latest
48+
environment:
49+
name: github-pages
50+
url: ${{ steps.deployment.outputs.page_url }}
51+
steps:
52+
- id: deployment
53+
uses: actions/deploy-pages@v4

‎.gitignore‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
_site/
3+
.content/
4+
.DS_Store
5+
*.log

‎LICENSE‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 proofstone
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

‎README.md‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# proofstone.dev
2+
3+
The umbrella site for the **proofstone** engineering roadmaps — roadmaps where every
4+
node is a milestone you can *prove* you passed (an artifact), not a keyword.
5+
6+
**The roadmap repositories are the source of truth. This repo is the render layer.**
7+
Each roadmap's `README.md` is fetched at build time and rendered into a page. You
8+
contribute to the *content* by opening a pull request on a roadmap's own repo; the
9+
site rebuilds itself.
10+
11+
Static site: [Eleventy](https://www.11ty.dev/) → GitHub Pages. No backend, no CMS,
12+
no tracking, no cookies.
13+
14+
## Run locally
15+
16+
```
17+
npm install
18+
npm run dev # fetches roadmap content, then serves at http://localhost:8080
19+
```
20+
21+
Offline (use already-fetched content in `.content/`):
22+
23+
```
24+
npm run dev:offline
25+
```
26+
27+
Build for production into `_site/`:
28+
29+
```
30+
npm run build # SITE_NOINDEX defaults to "true" (pre-launch)
31+
```
32+
33+
## Connect a new roadmap (target: a few minutes)
34+
35+
1. Add one entry to [`roadmaps.config.mjs`](roadmaps.config.mjs) — `slug`, `repo`,
36+
`branch`, `title`, `tagline`, `status: 'live'`. (`slug` = repo name without the
37+
`-roadmap` suffix. Milestone count is read from the README automatically.)
38+
2. Nothing else to write — the fetch step is generic.
39+
3. In the roadmap's repo, add [`docs/notify-site.yml.tmpl`](docs/notify-site.yml.tmpl)
40+
as `.github/workflows/notify-site.yml` so content edits trigger a site rebuild.
41+
4. Commit and push — the site build fetches and renders it.
42+
43+
## How content stays in sync
44+
45+
The build (`.github/workflows/build-deploy.yml`) runs on: a push here · a
46+
`repository_dispatch` fired by a roadmap repo when its README/assets change · a
47+
nightly schedule (safety net) · manual dispatch. Each run re-fetches every live
48+
roadmap, so a content edit lands on the site with no manual steps.
49+
50+
## noindex / launch
51+
52+
The site ships with `<meta name="robots" content="noindex, nofollow">` on every page
53+
while `SITE_NOINDEX` is unset or `"true"`. Launch = build with `SITE_NOINDEX=false`
54+
(or set the repo variable `SITE_NOINDEX` to `false`). robots.txt intentionally allows
55+
crawling so the noindex tag is seen.
56+
57+
## Structure
58+
59+
```
60+
roadmaps.config.mjs # roadmap registry (single source of truth for the site)
61+
eleventy.config.mjs # markdown render (GitHub-compatible anchors + link rewrite)
62+
scripts/fetch-content.mjs# fetch-at-build: README + assets per live roadmap → .content/
63+
src/ # templates, data, assets (css/js/favicon)
64+
docs/notify-site.yml.tmpl# drop-in workflow for a roadmap repo
65+
```
66+
67+
## License
68+
69+
Site code: MIT. Each roadmap's content is licensed in its own repository.

‎docs/notify-site.yml.tmpl‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# ─────────────────────────────────────────────────────────────────────────────
2+
# Connect-a-roadmap step 3 (ARCHITECTURE.md §4).
3+
# Copy this into a ROADMAP repo as: .github/workflows/notify-site.yml
4+
# It pings the proofstone site to rebuild whenever this roadmap's README or
5+
# assets change — so a content edit lands on the site with no manual steps.
6+
#
7+
# One-time setup: add a secret PROOFSTONE_DISPATCH_TOKEN to this repo (or the org)
8+
# = a fine-grained PAT scoped to the SITE repo with "Contents: read & write"
9+
# (the scope GitHub requires to POST repository_dispatch). Nothing else needed.
10+
#
11+
# Set OWNER/SITE_REPO below to the site repo created in Ф3 (recommended:
12+
# proofstone/proofstone.dev).
13+
# ─────────────────────────────────────────────────────────────────────────────
14+
name: notify-site
15+
16+
on:
17+
push:
18+
branches: [main]
19+
paths:
20+
- 'README.md'
21+
- 'assets/**'
22+
23+
jobs:
24+
dispatch:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- name: Tell the proofstone site to rebuild
28+
run: |
29+
curl -sf -X POST \
30+
-H "Accept: application/vnd.github+json" \
31+
-H "Authorization: Bearer ${{ secrets.PROOFSTONE_DISPATCH_TOKEN }}" \
32+
"https://api.github.com/repos/proofstone/proofstone.dev/dispatches" \
33+
-d '{"event_type":"roadmap-content"}'

‎eleventy.config.mjs‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import markdownIt from 'markdown-it';
2+
import markdownItAnchor from 'markdown-it-anchor';
3+
import GithubSlugger from 'github-slugger';
4+
import { roadmaps } from './roadmaps.config.mjs';
5+
6+
// One slugger per rendered document (GitHub-compatible slugs + dedup). Reassigned
7+
// before each roadmap render so anchors match the source README exactly.
8+
let slugger = new GithubSlugger();
9+
10+
const md = markdownIt({ html: true, linkify: true, typographer: false }).use(
11+
markdownItAnchor,
12+
{
13+
slugify: (s) => slugger.slug(s),
14+
tabIndex: false,
15+
permalink: markdownItAnchor.permalink.linkInsideHeader({
16+
symbol: '#',
17+
placement: 'after',
18+
class: 'ps-anchor',
19+
ariaHidden: true
20+
})
21+
}
22+
);
23+
24+
// Rewrite a single relative URL from a roadmap README to a working site/GitHub URL.
25+
function rewriteUrl(url, roadmap) {
26+
if (!url) return url;
27+
if (url.startsWith('#')) return url; // in-page anchor
28+
if (/^(https?:)?\/\//i.test(url) || url.startsWith('mailto:') || url.startsWith('data:')) return url;
29+
const clean = url.replace(/^\.\//, '');
30+
if (clean.startsWith('assets/')) return `/${roadmap.slug}/${clean}`; // local copied asset
31+
return `https://github.com/${roadmap.repo}/blob/${roadmap.branch}/${clean}`; // repo file → GitHub
32+
}
33+
34+
function rewriteLinks(html, roadmap) {
35+
return html
36+
.replace(/(<a\b[^>]*\shref=")([^"]*)(")/g, (_m, a, u, c) => a + rewriteUrl(u, roadmap) + c)
37+
.replace(/(<img\b[^>]*\ssrc=")([^"]*)(")/g, (_m, a, u, c) => a + rewriteUrl(u, roadmap) + c);
38+
}
39+
40+
export default function (eleventyConfig) {
41+
// Global chrome assets.
42+
eleventyConfig.addPassthroughCopy({ 'src/assets': 'assets' });
43+
44+
// Custom-domain marker. Actions deploys do NOT auto-create a CNAME file
45+
// (GitHub Docs), so we ship one to keep proofstone.dev bound on every deploy.
46+
eleventyConfig.addPassthroughCopy({ 'src/CNAME': 'CNAME' });
47+
48+
// Per-roadmap assets fetched into .content/<slug>/assets → /<slug>/assets/…
49+
for (const r of roadmaps) {
50+
eleventyConfig.addPassthroughCopy({ [`.content/${r.slug}/assets`]: `${r.slug}/assets` });
51+
}
52+
53+
// Render a roadmap README to HTML with GitHub-compatible anchors + rewritten links.
54+
eleventyConfig.addFilter('roadmapMarkdown', (content, roadmap) => {
55+
slugger = new GithubSlugger(); // reset dedup state per document
56+
const html = md.render(content || '');
57+
return rewriteLinks(html, roadmap);
58+
});
59+
60+
eleventyConfig.addWatchTarget('./src/assets/');
61+
62+
return {
63+
dir: { input: 'src', includes: '_includes', data: '_data', output: '_site' },
64+
markdownTemplateEngine: 'njk',
65+
htmlTemplateEngine: 'njk'
66+
};
67+
}

0 commit comments

Comments
 (0)