generated from alex/base-template
feat: self-contained Deno app mirroring PDS avatar → Gravatar #2
No reviewers
Labels
No labels
dependencies
No milestone
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
alex/atproto-gravatar-sync!2
Loading…
Reference in a new issue
No description provided.
Delete branch "pi/issue1-1783629626256"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #1.
Implements the full plan: a zero-dependency Deno app that keeps a Gravatar profile picture in sync with the account's Bluesky/ATProto avatar, packaged as a podman container.
What's included
Sync logic (
lib/+main.ts)lib/pds.ts—PdsClient: public PDS xrpc reads (resolveHandle,getRecord→ avatar cid/mime,getBlob). No PDS auth needed.lib/gravatar.ts—GravatarClient:list/upload/activate/delete/cleanup+emailHash(SHA-256 of normalised email). Bearer-token auth.lib/state.ts—StateStore: persists the last-synced cid on the mounted volume.main.ts—loadConfig(env, injectable for tests),sync()orchestration,main()entry.Each run resolves handle → DID, reads the avatar blob ref, skips if the content-addressed cid is unchanged, else fetches the blob → uploads → activates → best-effort prunes stale images → persists the cid. The freshly uploaded image is protected by identity, so a flaky
selectedflag can never delete it.Tests (
main_test.ts) — 7 tests, all using an in-process fakefetch(no real network):selectedflagloadConfigmissing-vars rejection + defaults/trailing-slash trimmingPackaging & ops
Containerfile— multi-stagedeno compile→ standalone binary ondebian:stable-slim(+ ca-certificates).compose.yml— optional local/manual runs.systemd/gravatar-avatar-sync.{service,timer}— oneshotpodman run --rm, daily +Persistent=true..env.example,.gitignore..forgejo/workflows/ci.yml—fmt --check/lint/check/test+ a container build job (the existingpi.ymlis untouched).README.md— what it does, env-var table, how to run (podman / compose / systemd), token generation.Notes
deno.lockto commit.deno fmt --check,deno lint,deno check main.tsclean;deno test→ 7 passed;deno compileproduces a working binary (exits 1 with a clear message on missing env). The systemd units parse cleanly. The container build couldn't run inside this sandbox (buildkit needscatatonit), but thedeno compilestep it performs was verified directly./pi fix the type errors, see https://forge.l3x.in/alex/atproto-gravatar-sync/actions/runs/6/jobs/0/attempt/1#jobstep-5-1
Push changes back into the PR
Now let me look at the test file and then reproduce the type error locally.Let me get the CI status with the correct repo context and fetch the failing logs.The API isn't returning the logs. Let me install Deno and reproduce the type error locally.The check passed with Deno 2.1.4, but CI uses the latest version via
setup-deno@v2. Let me try with the latest version to reproduce.I reproduced the type error. This is the well-known TypeScript 5.7+ change whereUint8Arrayis now generic over its buffer type (Uint8Array<ArrayBufferLike>), butBlobPartrequiresUint8Array<ArrayBuffer>. Let me check the full extent of the type errors across the project.I've reproduced both type errors. This is the TypeScript 5.7+ change whereUint8Arraybecame generic over its buffer type (Uint8Array<ArrayBufferLike>), butBlob/Responsebody types requireUint8Array<ArrayBuffer>. There are two error sites:lib/gravatar.ts:62—new Blob([bytes], ...)main_test.ts:93—new Response(state.avatar.bytes, ...)Let me check the config and producers of the byte arrays to choose the right fix.Let me verify the cross-version compatibility of different fix approaches. First, check the TS versions and test the generic-typing approach on older Deno.Confirmed: the generic
Uint8Array<ArrayBuffer>syntax breaks older Deno (TS2315). Let me test a cross-version-safe coercion approach.I have the full picture now. The errors stem from TypeScript 5.7+ makingUint8Arraygeneric (Uint8Array<ArrayBufferLike>), which isn't assignable toBlobPart/BodyInit(they needUint8Array<ArrayBuffer>). I confirmed the generic-syntax fix (Uint8Array<ArrayBuffer>) breaks older Deno (TS2315), so I'll use a cross-version-safe coercion via.slice(), which returnsUint8Array<ArrayBuffer>and compiles on all Deno versions.Let me apply the fix to both sites.Now let me verify the fix compiles and tests pass on all three Deno versions (old, mid, latest/CI).All green across all three Deno versions (2.1.4, 2.4.0, and 2.9.2 — the latest that CI's
setup-deno@v2installs). The previously-failingdeno checknow passes, fmt/lint clean, and all 7 tests pass. Let me review the final diff.The fix is complete and verified. Now let me push the changes back into the PR.Fixed the type errors reported by CI.Root cause: TypeScript 5.7 (shipped in Deno 2.2+) made
Uint8Arraygeneric over its buffer type, so a bareUint8Arrayis now inferred asUint8Array<ArrayBufferLike>. That isn't assignable toBlobPart/BodyInit, which requireUint8Array<ArrayBuffer>. CI'ssetup-deno@v2pulls the latest Deno (2.9.2 / TS 6.0), so it caught two errors that older Deno did not:lib/gravatar.ts:62—new Blob([bytes], ...)inupload()main_test.ts:93—new Response(state.avatar.bytes, ...)in the fakegetBlobFix: Coerce with
.slice(), which returns aUint8Array<ArrayBuffer>(copying into a realArrayBuffer). I deliberately avoided theUint8Array<ArrayBuffer>type annotation because it errors as "Type 'Uint8Array' is not generic" (TS2315) on Deno <2.2, whereas.slice()compiles cleanly everywhere.Verification (all three against Deno 2.1.4, 2.4.0, and 2.9.2/CI):
deno fmt --check✅deno lint✅deno check main.ts main_test.ts✅ (previously failed on 2.9.2)deno test→ 7 passed ✅Changes pushed to the PR.
View action run | Model: zai/glm-5.2 (thinking: xhigh) | Time: 6m 54s | Tokens: 328.3K | Pi SDK v0.80.3 | Action v2.25.1-develop (develop @ 5c04899)