5 Commits

Author SHA1 Message Date
0a9ef90160 Publish two overdue posts (flip draft: false)
Both reached their pubDate but were still draft: true, so every daily
scheduled-deploy rebuild skipped them — the gate in src/utils/posts.ts
checks draft before pubDate, so the flag outranks the date indefinitely.
The Jul 6 flip (c321728) covered only the posts due that day; these two
were not yet due and were never revisited:

- caldav-colors            (2026-07-08, 17 days overdue)
- the-feature-i-said-no-to (2026-07-10, 15 days overdue)

Verified with a production build: both render and appear in the blog
index, RSS feed, and sitemap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 09:57:45 +02:00
c321728b7f Publish three due posts (flip draft: false)
All checks were successful
scheduled-deploy / trigger-deploy (push) Successful in 25s
These posts had reached their pubDate but were still draft: true, so the
daily scheduled-deploy rebuild left them hidden — the publish gate in
src/utils/posts.ts checks draft before pubDate. Flip them to draft: false
so the date gate can release them:

- when-it-stopped-being-mine (2026-07-03)
- recurring-event-edit       (2026-07-05)
- the-translator-who-emailed-me (2026-07-06)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 19:34:23 +02:00
3c8e996b3e Merge pull request 'Document blog authoring + release workflow' (#4) from docs-blog-workflow into main
All checks were successful
scheduled-deploy / trigger-deploy (push) Successful in 23s
2026-07-01 19:54:18 +00:00
0c686a2e40 Document how to add and release a blog post
docs/blog-workflow.md: frontmatter/voice conventions, the draft+pubDate gate,
the daily deploy cron, publish checklists (scheduled vs immediate), and gotchas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 21:44:12 +02:00
6776b1f337 Merge pull request 'Publish first post: Compose for widgets, RemoteViews underneath' (#3) from release-widgets-post into main 2026-07-01 19:39:21 +00:00
6 changed files with 122 additions and 5 deletions

117
docs/blog-workflow.md Normal file
View File

@@ -0,0 +1,117 @@
# Writing and releasing a blog post
How posts live, how they're scheduled, and how they go live. The short version:
**a post is public when it is `draft: false` *and* its `pubDate` has arrived**
and a daily job rebuilds the site so "arrived" keeps advancing on its own.
## 1. Add the post
Create a Markdown file in `src/content/blog/`, e.g.
`src/content/blog/my-post.md`. The filename (minus `.md`) is the URL slug:
`/blog/my-post/`.
Frontmatter is validated at build time against `src/content.config.ts`:
```markdown
---
title: Short, specific title
description: >-
One or two sentences. Shown in listings, the RSS feed, and social previews.
pubDate: 2026-07-08 # ISO date. The publish gate (see below).
# updatedDate: 2026-07-10 # optional; shown as "updated"
tags: [android, calendula] # see the tag vocabulary below
draft: true # true = never public; flip to false when ready
---
Body in Markdown. ~80-char wrapped to match the other posts.
```
Voice conventions (match the existing posts): first-person, "constraint as
design", link to standards/APIs inline, one punchy closing line. Reference other
posts with root-relative links like `/blog/open-standards`.
Established **tag vocabulary** — reuse these rather than inventing one-offs:
`android`, `architecture`, `calendula`, `caldav`, `open-standards`,
`open-source`, `accessibility`, `localization`, `meta`.
## 2. The three states
The gate lives in `src/utils/posts.ts` (`getPublishedPosts()`), which every
listing, the tag pages, the post routes, and `rss.xml` use.
| `draft` | `pubDate` | Dev server | Production |
| --- | --- | --- | --- |
| `true` | any | visible (preview) | **hidden** |
| `false` | future | visible (preview) | **hidden until the date** |
| `false` | today / past | visible | **live** |
So:
- **`draft: true`** — work in progress or not yet approved. Never public.
- **`draft: false` + future `pubDate`** — approved and *scheduled*. Publishes
itself on that day (see §3).
- **`draft: false` + past `pubDate`** — live now.
Preview anything (including drafts and scheduled posts) locally with
`npm run dev` — the dev server lifts the gate entirely.
## 3. How release actually happens
The site is static, so "now" is frozen at each build. Two things trigger a
rebuild:
1. **Push to `main`** → Coolify redeploys (the normal deploy).
2. **The daily cron**`.gitea/workflows/scheduled-deploy.yml` runs at
**06:15 Europe/Berlin**, pings the Coolify deploy hook, and the rebuild
re-evaluates the date gate. This is what makes a future-dated post appear on
its day without anyone touching the repo.
The cron runs on the shared **`docker`** runner and needs two repo secrets set
once (Gitea → Settings → Actions → Secrets): `COOLIFY_DEPLOY_HOOK` (the app's
deploy URL) and `COOLIFY_TOKEN` (a Coolify API token, Bearer).
## 4. Publish checklist
To schedule a post for its date (the normal path):
1. Set `draft: false`, leave the intended future `pubDate`.
2. Commit and get it onto `main` (branch → PR → merge; PRs via `tea pr create`
/ `tea pr merge`). Merging deploys, but the post stays hidden until its date.
3. Done — the daily cron publishes it on `pubDate`.
To release a post **immediately**:
1. Set `draft: false` **and** `pubDate` to today (or a past date), so the gate
passes now.
2. Merge to `main`.
3. Trigger a rebuild right away instead of waiting for the cron:
```sh
tea actions workflows dispatch scheduled-deploy.yml --ref main
```
(`tea` may print `unexpected end of JSON input` — that's it mis-reading
Gitea's empty success response; the run still starts. Check it with
`tea actions runs list`.)
4. Coolify rebuilds; confirm at `https://jeanlucmakiola.de/blog/<slug>/`.
## 5. Handy commands
```sh
npm run dev # preview everything locally
npm run build # production build (applies the gate)
tea actions workflows list # see the deploy workflow
tea actions workflows dispatch scheduled-deploy.yml --ref main # manual deploy
tea actions runs list # watch run status/conclusion
```
## 6. Gotchas
- **Don't** put non-post `.md` files under `src/content/blog/` — the collection
glob will try to parse them as posts and fail the build. Docs like this one
live in `docs/`.
- `pubDate` with no time resolves to `00:00 UTC` that day, so a post becomes
eligible at UTC midnight; the morning cron then publishes it. Adjust the cron
time in the workflow if you want a different local hour.
- Timezone for the cron is pinned in the workflow (`TZ=Europe/Berlin`); the
gate comparison itself uses the build machine's clock (UTC in CI).

View File

@@ -6,7 +6,7 @@ description: >-
how event colour flows through Android's calendar provider. how event colour flows through Android's calendar provider.
pubDate: 2026-07-08 pubDate: 2026-07-08
tags: [android, calendula, caldav, accessibility] tags: [android, calendula, caldav, accessibility]
draft: true draft: false
--- ---
Two colour reports landed on Calendula in the same week, and both are still open. Two colour reports landed on Calendula in the same week, and both are still open.

View File

@@ -6,7 +6,7 @@ description: >-
it" surprisingly hard. it" surprisingly hard.
pubDate: 2026-07-05 pubDate: 2026-07-05
tags: [android, calendula, caldav] tags: [android, calendula, caldav]
draft: true draft: false
--- ---
A recurring event isn't a list of events. It's *one* row plus a rule. "Every A recurring event isn't a list of events. It's *one* row plus a rule. "Every

View File

@@ -6,7 +6,7 @@ description: >-
the app is actually for. the app is actually for.
pubDate: 2026-07-10 pubDate: 2026-07-10
tags: [open-source, calendula, open-standards] tags: [open-source, calendula, open-standards]
draft: true draft: false
--- ---
One of the more useful things that has happened to Calendula wasn't a feature I One of the more useful things that has happened to Calendula wasn't a feature I

View File

@@ -6,7 +6,7 @@ description: >-
emailed me directly. By the next morning: Italian at 40%. emailed me directly. By the next morning: Italian at 40%.
pubDate: 2026-07-06 pubDate: 2026-07-06
tags: [open-source, calendula, localization] tags: [open-source, calendula, localization]
draft: true draft: false
--- ---
If you want your app translated, the conventional wisdom is: lower the barrier. If you want your app translated, the conventional wisdom is: lower the barrier.

View File

@@ -6,7 +6,7 @@ description: >-
with a small community around it. with a small community around it.
pubDate: 2026-07-03 pubDate: 2026-07-03
tags: [open-source, calendula] tags: [open-source, calendula]
draft: true draft: false
--- ---
I built Calendula for myself. I wanted a fast, good-looking, privacy-respecting I built Calendula for myself. I wanted a fast, good-looking, privacy-respecting