Nightly update pulls for coolify/immich kept hitting transient ghcr.io
toomanyrequests errors and getting skipped, because the timer fired at
exactly 02:00:00 Europe/Amsterdam (the same instant countless other
hosts' cron/timer jobs also hit registries) and the retry loop only
spanned ~15s (3 attempts, 5/10/15s backoff) — not enough to ride out
the burst.
- Widen pull retries to 5 attempts with 20/40/60/80s backoff (~3+ min).
- Add RandomizedDelaySec=1800 to the update timer (and its install
template) so this host's pulls no longer land on the exact
top-of-hour thundering herd.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XmtksEEYSBZqUacw3qF1A9
Pauses Spotify via its MPRIS D-Bus interface. Talks to the user bus
socket directly ($XDG_RUNTIME_DIR/bus) since triggershell.service starts
before the graphical session and lacks DBUS_SESSION_BUS_ADDRESS. No-op
when Spotify is not running.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uCHmJ3PGcHZGtx8mYqmQG
Two opposite failure modes in `update run`:
- pulsenode was reported "updated" every night though its image never
changed: the pre-pull image-ID snapshot came back empty (the awk
fallback used \s, which mawk silently ignores), and empty != populated
was read as "changed".
- coolify was never recreated despite a new image: its nightly pull kept
hitting a transient `toomanyrequests`, and a failed pull only warned
before falling through to the comparison, which then saw no change.
Changes:
- _stack_image_ids: POSIX [[:space:]] instead of \s; return non-zero when
images can't be resolved so callers can tell "unknown" from "unchanged".
- Retry `docker compose pull` 3x with backoff; if it still fails, add the
stack to a new `skipped` list and skip the recreate instead of treating
it as up to date.
- Detect changes by comparing running containers' image IDs against the
post-pull resolved config, so a transient empty read can't be mistaken
for "everything changed".
- Notify with an amber "finished with warnings" summary listing skipped
stacks.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Launches Spotify via systemd-run --user so it inherits the graphical
session environment (the triggershell service starts before the Wayland
session and lacks WAYLAND_DISPLAY/DISPLAY). Idempotent: no-op when
spotify.service is already active.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHvz6Nxkf9SU2EufjWZ3cG
- system-shutdown / system-reboot / system-cancel-shutdown scripts with a
typed-confirmation guard and optional delay (works without sudo via logind)
- openrgb-hue-apply: inline the wrapper script into bash -c, keeping the PATH
fallback and install hint; drop scripts/openrgb-hue-apply.sh
- openrgb-hue-off: new script wrapping `openrgb-hue off` with device/type/zone
filters
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ZC9uGoYPWeG3hed7FpmJw
wayvnc exits with status 0 when it loses its capture output (e.g. the
idle daemon powering the screen off), so Restart=on-failure never
brought it back. Restart=always + StartLimitIntervalSec=0 keeps it
retrying until the output returns.
update run silently swallowed pull errors and never logged what actually
differed, making a recurring false "updates found" for pulsenode (no
registry-side change since Aug 19) impossible to diagnose.
systemd --user services get a minimal PATH that never sources shell
rc files, so pyenv shims and pip --user installs weren't found even
though openrgb-hue was installed. Prepend the common user-install
locations before the PATH lookup.
Drop the triggershell init scaffold scripts and add one script that
drives openrgb-hue apply: full option set as form fields, scene as a
103-entry dropdown, mapping defaulting to per-zone. The scene name
goes through a small wrapper since it's a positional CLI arg and
TriggerShell's passAs:arg can only emit --flag value pairs.
Firefox isn't installed; SUPER+W now launches google-chrome-stable,
and the opacity-override window rule matches Chrome's window class too.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a headless OpenRGB autostart entry plus a launcher wrapper that
focuses the existing GUI window instead of spawning a duplicate
instance, and tracks the customized RQuickShare autostart/launcher
desktop entries.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
glow v2.1.2 validates the style path with ~ expanded but passes the
original unexpanded string to glamour's file reader, causing ENOENT.
Fix: pass the style via --style flag so bash expands $HOME before glow
sees it. Revert glow.yml style to "auto" (unused when arty drives it).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add .config/glow/nord.json — full glamour style using Nord palette
(Polar Night bg, Frost headings, Aurora syntax, Snow Storm text)
- Configure glow.yml to use the Nord style by default
- Track both glow files in .gitignore whitelist
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New generate_notes_md(): builds title, description blockquote,
dependencies table, per-script fenced blocks, and details section
from the entire arty.yml using existing yq helpers
- show_notes() now accepts --raw to print raw Markdown; formatted
mode pipes through the same glow/mdcat/python3 render pipeline
- Removes the decorative ━━━ border (clashes with full-document output)
- main() passes args[] through to show_notes for flag forwarding
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prompts for name and email, writes [user] section to ~/.gitconfig.local
which is already included by .gitconfig.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>