Skip to content

Atomic writes & idempotency

Shotty treats the target PNG file like a build artefact that other tools (file watchers, dev servers, Git, CI) might be observing. Three guarantees flow from that.

1. Atomic writes — no half-written PNGs

Renders are written to a hidden temp file in the same directory as the target (.foo.png.shotty.<pid>.tmp), then rename(2)d into place once the encoder finalizes. If the render or encode fails, the temp file is unlinked; the existing target stays intact and untouched.

A reader holding the target PNG always sees a complete, valid file — never a partially-written one. The temp lives in the same directory so the rename is atomic on POSIX filesystems (cross-device renames aren't atomic).

2. Refuse to overwrite anything that isn't already a PNG

Before doing any work, Shotty checks the target path:

  • If it doesn't exist → proceed.
  • If it's a directory → abort with a clear error.
  • If it's a regular file but the first 8 bytes aren't the PNG magic number (89 50 4E 47 0D 0A 1A 0A) → abort.

So pointing -o at, say, your ~/.zshrc won't silently destroy it. You'd have to delete the file manually first.

3. Skip the rename if pixels are identical

After rendering into memory but before the rename, Shotty checks whether the target file already exists. If it does, the existing PNG is decoded back into 8-bit RGBA and compared to the new render with memcmp. If the bytes match, the rename doesn't happen:

  • The temp file is removed.
  • The target's mtime stays unchanged.
  • Shotty prints 💤 path/to.png unchanged (...).

This is what makes shotty --render-all genuinely idempotent: re-running it is a no-op when nothing has changed. File watchers stay quiet, Git's working tree stays clean, ReadTheDocs and similar build systems don't see spurious changes, and CI can use git diff --exit-code as a "docs are out of date" gate.

The comparison happens in pixel space, not file-bytes space, because two PNG encoders (or two runs of the same encoder with different zlib settings) can produce different file bytes for the same image. Decoding both sides into a normalized RGBA buffer is the only reliable way to say "these two PNGs would look identical."

See also