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
42 changes: 0 additions & 42 deletions .github/workflows/notify-new-post.yml

This file was deleted.

11 changes: 10 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ just dev # Start dev server with auto-open
# Build & Deploy
just build # Production build
just deploy # Deploy to Cloudflare Pages (staging by default)
just deploy live # Deploy to production
just deploy live # Deploy to production, and email subscribers about new posts
just prod # Build and preview production locally

# Code Quality
Expand Down Expand Up @@ -52,6 +52,15 @@ just fix # Auto-fix code formatting/lint issues

- Cloudflare Pages via Wrangler
- `just deploy` for staging, `just deploy live` for production
- Deploys are manual; nothing ships on merge to `main`

**`just deploy live` mails the subscriber list.** `scripts/notify-subscribers.ts` snapshots the slugs in `https://moq.dev/rss.xml` before the upload, then sends a Resend broadcast for every post in the freshly built `dist/rss.xml` that wasn't in that snapshot. Subject and body come from the feed's `title` and `description`. A deploy that adds no posts sends nothing.

Credentials come from 1Password, so no secret has to sit on disk. `op.env` maps `RESEND_API_KEY` to `op://Corp/Resend/credential` and `op run` resolves it for the duration of the command. That file is committed on purpose: it holds references, not values. Install and sign in once with `brew install 1password-cli && op signin`.

Without the 1Password CLI the recipe falls back to `RESEND_API_KEY` and `RESEND_SEGMENT_ID` from the ambient environment, and if those are missing too the deploy still succeeds while the script exits non-zero to say the announcement did not go out. `just deploy staging` never announces and never touches 1Password.

Broadcasts cannot be recalled, so the script refuses to guess: an unreachable or empty live feed, a missing snapshot, or a missing build all skip sending rather than risk mailing the back catalogue. Any local `.mdx` under `src/pages/blog/` ships on the next `just deploy live` and gets announced, drafts included.

## Development Tips

Expand Down
25 changes: 25 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,33 @@ build mode="live":
bun astro build --mode {{mode}}

# Deploy the site to Cloudflare Pages
# On `live`, any post that wasn't already on moq.dev gets mailed to subscribers.
deploy env="staging": (build env)
# Record what's live before we replace it, so we can tell what the deploy added.
bun scripts/notify-subscribers.ts snapshot --env {{env}}
bun wrangler deploy --env {{env}}
just _announce {{env}}

# Mail subscribers about anything this deploy published.
# Credentials come from 1Password (see op.env) so no secret has to live on disk.
[private]
_announce env:
#!/usr/bin/env bash
set -euo pipefail

# Staging never announces, so don't make it depend on 1Password.
if [ "{{env}}" != "live" ]; then
exec bun scripts/notify-subscribers.ts send --env {{env}}
fi

# Fall back to the ambient environment rather than failing outright: the deploy
# has already happened by now, and the script reports a missing key itself.
if ! command -v op >/dev/null 2>&1; then
echo "[notify] 1Password CLI not found, falling back to the ambient environment." >&2
exec bun scripts/notify-subscribers.ts send --env {{env}}
fi

exec op run --env-file=op.env -- bun scripts/notify-subscribers.ts send --env {{env}}

dev:
bun i
Expand Down
14 changes: 14 additions & 0 deletions op.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Secret references for `just deploy live`, resolved by `op run`.
#
# This file is committed on purpose: it holds pointers, not secrets. `op run`
# swaps each op:// reference for the real value at run time and masks it in the
# output. Nothing here is sensitive on its own.
#
# Requires the 1Password CLI, signed in to an account with the Corp vault:
# brew install 1password-cli && op signin

RESEND_API_KEY=op://Corp/Resend/credential

# The "Blog" segment. Not a secret, just an id, so keep it inline rather than
# minting a 1Password item for it.
RESEND_SEGMENT_ID=7bfda95d-9eb6-4f29-b1f8-1c2a748ce2ff
Loading
Loading