Skip to content

Commit 3a1c2e7

Browse files
jebin2claude
andcommitted
feat: merge the blog repo into this one, build content locally
Content now lives in content/, brought over with its full git history so post dates survive — created_date comes from each file's first commit, and a plain copy would have reset nine months of publication dates to today. Verified: all 8 posts and 9 svgs regenerate with byte-identical dates and ordering against the manifest the old pipeline produced. What this removes: - the cross-repo repository_dispatch and its PAT - the jsDelivr purge step and the ~12h CDN staleness it worked around - the daily cron, which only existed to catch content changes here - uncachedUrl() in both build scripts The build now reads everything off disk instead of over the network, so it works offline and can't be broken by an upstream outage. Assets are served from this repo by Pages; og:image is absolutised for unfurlers. scripts/generate-manifest.mjs ports the blog repo's Python indexer, including the hybrid history scan (fast global pass, --follow fallback for renamed files) that makes those dates correct. Also: 404.html no longer answers an unknown URL with a full post listing — it renders a real not-found instead. .nojekyll stops Pages filtering content/ through Jekyll now that raw .md and assets are served from there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 47226f4 commit 3a1c2e7

19 files changed

Lines changed: 224 additions & 446 deletions

File tree

‎.github/workflows/sitemap.yml‎

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
name: Build site
22

3-
# Posts live in the blog repo, so prerendered pages and the sitemap go stale
4-
# without anything changing here. Rebuild daily, on demand, and whenever the
5-
# build inputs change.
3+
# Content lives in this repo now, so a push is the only thing that can change
4+
# the built output. No cron, no cross-repo trigger.
65
on:
7-
schedule:
8-
- cron: '0 3 * * *'
96
workflow_dispatch:
10-
# Sent by the blog repo right after it publishes, so a new post is
11-
# prerendered in about a minute instead of waiting for the daily run.
12-
repository_dispatch:
13-
types: [content-updated]
147
push:
158
branches: [main]
169
paths:
10+
- 'content/**'
1711
- 'config.json'
1812
- 'js/utils.js' # slug rule — changing it moves every post URL
1913
- 'scripts/**'
@@ -32,7 +26,7 @@ jobs:
3226
steps:
3327
- uses: actions/checkout@v4
3428
with:
35-
# Full history so lastmod can come from each page's last commit
29+
# Full history: post dates come from each file's commits
3630
fetch-depth: 0
3731

3832
- uses: actions/setup-node@v4
@@ -43,6 +37,9 @@ jobs:
4337
# client-side rendering of the same markdown agree.
4438
- run: npm install --no-save marked@9.1.6
4539

40+
- name: Index posts
41+
run: node scripts/generate-manifest.mjs
42+
4643
- name: Prerender posts
4744
run: node scripts/prerender.mjs
4845

@@ -51,12 +48,12 @@ jobs:
5148

5249
- name: Commit if changed
5350
run: |
54-
if git diff --quiet -- sitemap.xml writing/ && [ -z "$(git status --porcelain writing/)" ]; then
51+
if [ -z "$(git status --porcelain content/manifest.json writing/ sitemap.xml)" ]; then
5552
echo "nothing changed"
5653
exit 0
5754
fi
5855
git config user.name "github-actions[bot]"
5956
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
60-
git add -A sitemap.xml writing/
61-
git commit -m "chore: rebuild prerendered posts and sitemap"
57+
git add -A content/manifest.json writing/ sitemap.xml
58+
git commit -m "chore: rebuild manifest, post pages and sitemap"
6259
git push

‎.nojekyll‎

Whitespace-only changes.

‎README.md‎

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,57 +3,57 @@
33
The website — **https://www.voidall.com**. Static, no framework, no bundler, no runtime
44
dependencies. Plain HTML, one stylesheet, and native ES modules.
55

6-
The words live in a separate repo, [jebin2/blog](https://github.com/jebin2/blog). This repo
7-
holds everything that displays them, plus a small build that turns each post into a real
8-
static page.
6+
Posts live in `content/` in this same repo. Writing and publishing are one push.
97

108
---
119

12-
## How content gets here
10+
## Publishing a post
11+
12+
Create a folder under `content/` and a `.md` file inside it with **the same name**:
1313

14-
```mermaid
15-
flowchart LR
16-
A[jebin2/blog<br/>push a post] -->|repository_dispatch| B[Build site workflow]
17-
C[daily 03:00 UTC<br/>backup] -.-> B
18-
B --> D[prerender.mjs<br/>writing/slug/index.html]
19-
B --> E[generate-sitemap.mjs]
20-
D --> F[commit]
21-
E --> F
22-
F --> G[GitHub Pages]
14+
```
15+
content/C/Bitfields/Bitfields.md ✅ indexed as a post
16+
content/C/Bitfields/header.png ✅ image, referenced as ![](header.png)
17+
content/C/Bitfields/notes.md ❌ ignored — name doesn't match the folder
2318
```
2419

25-
`.github/workflows/sitemap.yml` (“Build site”) runs on four triggers:
20+
Push, and the build does the rest.
2621

27-
| Trigger | When |
28-
|---|---|
29-
| `repository_dispatch` | the blog repo publishes — arrives in ~2s |
30-
| `schedule` | daily 03:00 UTC, so a missed dispatch still gets picked up |
31-
| `workflow_dispatch` | manual run |
32-
| `push` | `config.json`, `js/utils.js` or `scripts/**` changed |
22+
```mermaid
23+
flowchart LR
24+
A[push content/] --> B[generate-manifest.mjs]
25+
B --> C[prerender.mjs<br/>writing/slug/index.html]
26+
C --> D[generate-sitemap.mjs]
27+
D --> E[commit]
28+
E --> F[GitHub Pages]
29+
```
3330

34-
It installs `marked`, prerenders every post, rebuilds the sitemap, and commits only if
35-
something actually changed.
31+
`.github/workflows/sitemap.yml` (“Build site”) runs on a push touching `content/`,
32+
`config.json`, `js/utils.js` or `scripts/**` — or manually. It indexes the posts,
33+
prerenders them, rebuilds the sitemap, and commits only if something actually changed.
3634

3735
### Running the build locally
3836

3937
```bash
4038
npm install --no-save marked@9.1.6
41-
node scripts/prerender.mjs # writes writing/<slug>/index.html
42-
node scripts/generate-sitemap.mjs # writes sitemap.xml
39+
node scripts/generate-manifest.mjs # writes content/manifest.json
40+
node scripts/prerender.mjs # writes writing/<slug>/index.html
41+
node scripts/generate-sitemap.mjs # writes sitemap.xml
4342
```
4443

45-
Both read post content from the origin (`raw.githubusercontent.com`) rather than the CDN, so
46-
a build always sees the newest Markdown. Output is deterministic — dates are formatted from
47-
the literal date in the manifest, not the machine's timezone, so CI and a laptop produce
48-
identical files.
44+
The build reads everything off disk — no network — so it works offline and can't be broken
45+
by an upstream outage. Output is deterministic: post dates come from git history, and the
46+
displayed date is formatted from the literal date in the manifest rather than the machine's
47+
timezone, so CI and a laptop produce identical files.
4948

5049
---
5150

5251
## Layout
5352

5453
| Path | |
5554
|---|---|
56-
| `config.json` | **the one file that says where content lives** — manifest, projects, links, and the base URL for post assets |
55+
| `content/` | **the posts**, their images, `data/*.json`, and the generated `manifest.json` |
56+
| `config.json` | the one file that says where content lives — now local paths under `/content/` |
5757
| `index.html` | post listing (home) |
5858
| `writing/index.html` | the writing listing |
5959
| `writing/<slug>/` | **generated** — one prerendered page per post |
@@ -62,6 +62,7 @@ identical files.
6262
| `css/style.css` | the whole stylesheet |
6363
| `js/*.js` | one module per page, plus shared helpers |
6464
| `scripts/*.mjs` | build scripts (Node, run in CI) |
65+
| `.nojekyll` | stops Pages filtering `content/` through Jekyll |
6566
| `sitemap.xml` | **generated** |
6667

6768
### The JavaScript
@@ -89,8 +90,8 @@ Prerendered post pages arrive with the article already in the HTML — a preview
8990
crawler or a reader with JS disabled all get the real post. JavaScript afterwards only adds
9091
the nav and footer, syntax highlighting, a read count, and (for admins) the share buttons.
9192

92-
The listing pages are still client-rendered: they fetch `manifest.json` through jsDelivr and
93-
two `get_stats` calls from Supabase — one for the all-time sort, one for the 24-hour counter
93+
The listing pages are still client-rendered: they fetch `/content/manifest.json` from this
94+
same origin and make two `get_stats` calls to Supabase — one for the all-time sort, one for the 24-hour counter
9495
shown next to each title.
9596

9697
If a post exists in the manifest but hasn't been prerendered yet, `/writing/<slug>/` 404s,
@@ -116,8 +117,11 @@ it from a CDN with `integrity` attributes; a stale hash blocks the script and po
116117
rendering on the fallback paths. Keep the pinned version in the workflow matching the one in
117118
the HTML so build-time and client-side rendering agree.
118119

119-
**Post URLs come from post titles.** Renaming a post in the blog repo moves its URL and
120-
prunes the old directory — the old link dies with no redirect.
120+
**Post URLs come from post titles.** Renaming a post moves its URL and prunes the old
121+
directory — the old link dies with no redirect.
122+
123+
**Post dates come from git history**, so never rewrite history under `content/` and never
124+
copy posts in without it. A plain copy resets every `created_date` to the day of the copy.
121125

122126
**`isAdmin()` is a localStorage flag**, trivially set by anyone. It only reveals share and
123127
export UI, nothing privileged. Don't let it grow into gating anything that matters.

‎config.json‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"name": "jebin2",
33
"github": "https://github.com/jebin2",
44
"sponsor": "https://github.com/sponsors/jebin2",
5-
"projects": "https://cdn.jsdelivr.net/gh/jebin2/blog@main/data/projects.json",
6-
"links": "https://cdn.jsdelivr.net/gh/jebin2/blog@main/data/links.json",
7-
"blog_manifest": "https://cdn.jsdelivr.net/gh/jebin2/blog@main/manifest.json",
8-
"blog_base_url": "https://cdn.jsdelivr.net/gh/jebin2/blog@main/"
5+
"projects": "/content/data/projects.json",
6+
"links": "/content/data/links.json",
7+
"blog_manifest": "/content/manifest.json",
8+
"blog_base_url": "/content/"
99
}

0 commit comments

Comments
 (0)