Skip to content

Latest commit

 

History

History
215 lines (163 loc) · 6.11 KB

scheduling.md

File metadata and controls

215 lines (163 loc) · 6.11 KB

Scheduling hoist

Hoist is meant to run periodically. The recommended way to set that up is the built-in subcommand:

hoist --cron install

This auto-detects the available scheduler and walks you through the prompts. The rest of this document is a reference for what --cron install actually writes, plus a manual setup recipe for macOS (which --cron does not install automatically).


hoist --cron cheat sheet

Command What it does
hoist --cron Interactive menu (install / remove / print / status / cancel)
hoist --cron install Detect scheduler, prompt, write files, enable
hoist --cron install --schedule hourly --user root --backend cron Fully non-interactive install
hoist --cron install --scope user --schedule hourly --docker-host unix:///run/user/1000/docker.sock Non-interactive user-scope install with an explicit rootless docker socket
hoist --cron remove Uninstall hoist-managed schedule (idempotent)
hoist --cron print Print files that would be written (no changes)
hoist --cron status Show what's currently installed; exit 1 if nothing
hoist --cron install --dry-run Show planned writes without touching disk

Schedule presets

Preset cron expression systemd OnCalendar
30min */30 * * * * *:0/30
hourly 0 * * * * hourly
6hourly 0 */6 * * * 0/6:00:00
daily 0 3 * * * *-*-* 03:00:00
weekly 0 3 * * 0 Sun *-*-* 03:00:00

You can also pass any raw cron expression (5 fields) or systemd OnCalendar value via --schedule.

Safety guarantees

  • All managed files start with a # Managed by hoist --cron install marker. Hoist refuses to overwrite a file at the same path without it.
  • Re-running --cron install silently replaces the previous managed file.
  • Writes are gated by sudo only when the target isn't writable as the current user.
  • --user is validated via getent passwd; non-root users not in the docker group get a warning (still installs — useful for rootless docker setups).

Linux: cron

hoist --cron install --backend cron writes /etc/cron.d/hoist:

# Managed by hoist --cron install — do not edit; re-run `hoist --cron install` to change.
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 * * * * root /usr/local/bin/hoist >> /var/log/hoist.log 2>&1

Logs go to /var/log/hoist.log. Tail it with:

sudo tail -f /var/log/hoist.log

Linux: systemd timer

hoist --cron install --backend systemd writes two units in /etc/systemd/system/, runs daemon-reload, then enable --now hoist.timer.

hoist.service:

# Managed by hoist --cron install — do not edit; re-run `hoist --cron install` to change.
[Unit]
Description=Hoist — auto-update Docker containers via labels
After=docker.service
Wants=docker.service

[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/hoist

hoist.timer:

# Managed by hoist --cron install — do not edit; re-run `hoist --cron install` to change.
[Unit]
Description=Run hoist on schedule

[Timer]
OnCalendar=hourly
Persistent=true

[Install]
WantedBy=timers.target

Inspect with:

systemctl list-timers hoist.timer
systemctl status hoist.service
journalctl -u hoist.service

User scope and rootless docker

--scope user writes ~/.config/systemd/user/hoist.{service,timer} and enables them with systemctl --user. No sudo, no system units. Run loginctl enable-linger $USER if you want the timer to fire when you're not logged in.

Rootless docker normally exposes its socket at unix:///run/user/$(id -u)/docker.sock and surfaces it to interactive shells via DOCKER_HOST in ~/.bashrc / ~/.zshrc. A systemd --user timer does not inherit that — so hoist handles it two ways:

  • Auto-pin: if DOCKER_HOST is set in the shell where you run hoist --cron install, and it's not already in systemctl --user show-environment, and no docker context is steering the connection, hoist bakes Environment=DOCKER_HOST=<value> into the generated hoist.service.
  • Explicit --docker-host <uri>: overrides auto-detect. Useful when DOCKER_HOST isn't set in the calling shell (Ansible, CI, scripted installs) or auto-detect would pick the wrong socket.
hoist --cron install --scope user --schedule hourly \
  --docker-host unix:///run/user/1000/docker.sock

--docker-host is rejected with system scope — the system unit doesn't go through this path.


macOS: launchd

hoist --cron does not install on macOS — Apple plist conventions vary across OS versions and the resulting file is too easy to get subtly wrong from a generator. Drop a plist in place yourself:

~/Library/LaunchAgents/com.sumguy.hoist.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.sumguy.hoist</string>

    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/bash</string>
        <string>/usr/local/bin/hoist</string>
    </array>

    <key>StartCalendarInterval</key>
    <dict>
        <key>Minute</key>
        <integer>0</integer>
    </dict>

    <key>StandardOutPath</key>
    <string>/tmp/hoist.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/hoist.log</string>

    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
    </dict>
</dict>
</plist>

Adjust paths for your install (Apple Silicon: /opt/homebrew/bin/bash; Intel: /usr/local/bin/bash). Hoist requires bash 4+ — the system bash 3.2 will not work, so install one with brew install bash and point the plist at it.

Load and enable:

launchctl load -w ~/Library/LaunchAgents/com.sumguy.hoist.plist

Check it ran:

launchctl list | grep hoist
tail -f /tmp/hoist.log

Remove:

launchctl unload -w ~/Library/LaunchAgents/com.sumguy.hoist.plist
rm ~/Library/LaunchAgents/com.sumguy.hoist.plist