Skip to content

Spec-correct publication metadata refresh + imgproxy env fix#406

Merged
coreyja merged 3 commits into
mainfrom
feat/imgproxy-env-and-defensive
Jun 5, 2026
Merged

Spec-correct publication metadata refresh + imgproxy env fix#406
coreyja merged 3 commits into
mainfrom
feat/imgproxy-env-and-defensive

Conversation

@byte-the-bot

Copy link
Copy Markdown
Collaborator

Two-commit follow-up to #404 that fixes both the missing publication cover and the strong-ref cascade behavior on future cover updates.

Why

After #404 merged, the bluesky workflow's init step ran with `IMGPROXY_URL=` (the repo secret didn't exist), my code built a relative URL, reqwest refused, and the publication record landed on the PDS without a cover blob. 43 documents now strong-reference that cidless publication.

Updating publication metadata in atproto is spec-correctly a cascade: change the publication's content → cid bumps → every `site.standard.document` that strong-refs it should be re-put with the new `StrongRef`. Lenient consumers deref by URI and ignore cid drift; strict consumers treat the strong ref as an invariant. We're doing it to spec.

Commit 1 — `Hardcode IMGPROXY_URL in bluesky workflow + treat empty env as missing`

  • `bluesky.yml`: drop `${{ secrets.IMGPROXY_URL }}`, hardcode `IMGPROXY_URL: https://img.coreyja.com\` at workflow level. imgproxy is public.
  • `required_env(name)`: rejects both unset and empty-string. Empty env vars no longer silently produce malformed URLs.

Commit 2 — `Spec-correct publication-metadata refresh: cover_synced + atproto_pub_cid drift detection`

Two pieces of state + sync logic that makes publication updates cascade fully through CI, with no operator steps.

`publications.toml` gains `cover_synced: bool` (default `false`):

  • init's idempotent fast path now also requires `cover_synced` to be `true`.
  • Set to `true` only after a successful cover-blob upload — failures leave it `false` so the next deploy retries.
  • Self-heals our current state: `cover_synced` is currently absent (= false) in `publications.toml`, so the next bluesky run re-puts the publication with the cover. `at_cid` bumps, gets committed back.

`BlogFrontMatter` gains `atproto_pub_cid: Option`:

  • Records which publication CID this doc's `publication: StrongRef` was pinned to at last sync.
  • Sync compares against `pub_cfg.at_cid`; drift triggers a re-put.

`sync_one` rewritten around two booleans:

  • `need_doc_put` = no doc yet OR pinned cid drifted OR we're about to write a bsky post (so we have a fresh doc cid for it)
  • `need_bsky_post` = no `bsky_url` AND post is recent (≥ cutoff)
  • Skip when neither needs doing.

What happens on the next deploy after this merges

  1. init sees `cover_synced` is missing/false → fetches the cover via imgproxy → re-puts the publication record with cover → new `at_cid` → `publications.toml` updated with new cid + `cover_synced=true`
  2. sync walks 43 historical docs. All have `atproto_pub_cid=None`. Drift detected on every one → each gets re-put with the new `StrongRef` → frontmatter updated with the new `atproto_pub_cid`
  3. Commit syndication updates pushes everything back to main

Future cover updates

Flip `cover_synced = false` in `publications.toml`, push, deploy. Same cascade runs. No manual ops.

Tests

  • Workspace clippy (`--all-targets -Dwarnings`) clean
  • All 17 `standard_site` tests pass; the `PublicationConfig` constructors and `BlogFrontMatter` literals updated to include the new fields

🤖 Generated with Claude Code

byte-the-bot and others added 2 commits June 5, 2026 09:02
The bluesky workflow's init step was pulling \`IMGPROXY_URL\` from a repo
secret that didn't exist, expanding to an empty string. The Rust code
read that empty string as a present-but-empty value, built a relative
imgproxy URL, and reqwest refused with "relative URL without a base."
Result: the publication record landed on the PDS without a cover blob.

Two-part fix:

1. Drop the secret reference and hardcode \`IMGPROXY_URL: https://img.coreyja.com\`
   at the workflow level. imgproxy is a public service the blog itself
   links into; nothing about it needs to live in repo secrets.

2. Add \`required_env(name)\` that rejects both unset and empty-string
   values. Future missing/typoed env vars produce a clear error instead
   of a malformed URL further down the call chain.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…_cid drift detection

Adds two new pieces of state and the sync logic that uses them to make
publication-record updates cascade through every document that strong-
references it — fully via the bluesky workflow, no manual ops steps.

\`publications.toml\` gains \`cover_synced: bool\` (default \`false\`):
  - init's idempotent fast path now requires this in addition to
    cached \`at_uri\`/\`at_cid\`. Set to \`true\` only after a *successful*
    cover-blob upload; an upload failure leaves it \`false\` so the next
    deploy retries.
  - Self-heals our current state: the publication record was created
    on the prior deploy without a cover (\`IMGPROXY_URL\` was empty), so
    \`cover_synced\` is absent (= \`false\`) → next deploy re-puts the
    publication with cover → new cid in \`at_cid\`.

\`BlogFrontMatter\` gains \`atproto_pub_cid: Option<String>\`:
  - Records the publication CID this document's \`publication: StrongRef\`
    was pinned to at last sync.
  - Sync compares against current \`pub_cfg.at_cid\`; drift triggers a
    re-put of the document with the refreshed strong-ref. Spec-correct:
    when the referent's content changes, every referrer gets re-emitted.

\`sync_one\` is rewritten around two booleans:
  - \`need_doc_put\` = no document yet OR pinned cid drifted OR we're
    going to put a bsky post (so we have a fresh doc cid for it)
  - \`need_bsky_post\` = no bsky_url AND post is recent (>= cutoff)
  - Skip when neither needs doing. Document re-put always re-writes
    \`atproto_pub_cid\` so future runs short-circuit until drift again.

Net effect when the next bluesky workflow runs after this merges:
  1. init re-puts the publication with cover → at_cid bumps → committed
  2. sync walks 43 existing documents → all have atproto_pub_cid=None
     → drift detected → all re-put with new cid → frontmatter updated
  3. Commit step pushes the new at_cid + 43 frontmatter updates back.

Future cover updates: flip cover_synced=false in publications.toml,
push, deploy. Same cascade runs again. No manual steps.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Earlier cargo fmt apparently didn't apply this hunk locally. CI's rustfmt
flagged the multi-line match form; condensed back to the standard
`match expr { ... }` shape.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coreyja coreyja merged commit 0eeebb5 into main Jun 5, 2026
6 checks passed
@coreyja coreyja deleted the feat/imgproxy-env-and-defensive branch June 5, 2026 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants