5.6 KiB
Skill: Supporting the updates.d Update Architecture in a Sibling Project
Overview
There are two ways for a sibling project to plug into MiniServer:
- The
siblingcommand (preferred). The user runssiblingand gives it the project's git URL. The repo is cloned into~/.local/share/siblings/<name>and itsinstall.sh(if any) is run once. On everyupdate,updates.d/71-siblings.shpulls each sibling and runs itsupdate.sh(if any). The sibling only needs those two optional scripts. - A custom hook. MiniServer's
updatescript sources every*.shfile found in~/.local/share/MiniServer/updates.d/in filename order. A sibling project can install its own numbered shell script into that directory during its own install step, for full control over how and when it updates.
Using the sibling Command
See sibling-project-format.md for the full rules, templates and testing.
In short, give the repo these optional files at its top level:
| File | When it runs |
|---|---|
install.sh |
Once, from the repo folder, when the user runs sibling add (and again if they re-run it) |
update.sh |
On every update, from the repo folder, after the repo has been pulled |
Both run with bash, and a non-zero exit is reported as a failure.
update.sh should be idempotent. If you have no update.sh, update just pulls the repo.
Commands:
sibling # asks for the git URL
sibling add <url> # same, without the prompt
sibling list # installed siblings, their URL and current commit
sibling remove <name> # deletes ~/.local/share/siblings/<name>
The rest of this document covers the custom-hook approach.
How It Works
When a user runs update:
- The MiniServer repo at
~/.local/share/MiniServeris pulled. - All
*.shfiles inupdates.d/are sourced, each in its own subshell, in lexicographic order. - Each script can see
$MINISERVER, but nothing defined by earlier hooks — sourceutils.shyourself if you need its helpers. Acd,set -eorexitin one hook can't affect the others. - A hook that exits non-zero is listed in the summary at the end of
update.
Because the scripts are sourced from within the cloned repo directory, any file
placed there by a sibling project's installer will survive git pull — git
does not delete untracked files on pull.
Naming Convention
Existing MiniServer hooks use a numeric prefix to control execution order:
| File | Purpose |
|---|---|
10-pacman.sh |
Arch system update |
12-migrations.sh |
One-time migrations from migrations/ |
20-aur.sh |
AUR packages |
30-pm2.sh |
pm2 |
40-cockpit.sh |
Cockpit |
70-claude.sh |
Claude Code setup |
71-siblings.sh |
Projects installed with sibling |
72-sandbox.sh |
Sandbox runtime and Hermes Agent |
80-firewall.sh |
Ports from every firewall.conf |
95-cleanup.sh |
pacman cache cleanup |
99-reboot-check.sh |
Reboot-needed notice |
Sibling projects should use an unused number from 73–79 so they run after the core updates but before MiniServer's end-of-run hooks (80+).
What to Add to a Sibling Project
1. Create the hook script
Add a file like updates.d/73-myproject.sh to the sibling repo:
#!/bin/bash
echo "Updating MyProject..."
git -C "$HOME/.local/share/myproject" pull
# Optional: use MiniServer helpers if needed
# source "$MINISERVER/utils.sh"
# install_packages "${MY_PACKAGES[@]}"
2. Install the hook during the sibling's install step
In the sibling's install.sh, copy the hook into MiniServer's updates.d/:
echo "Registering MyProject update hook..."
cp updates.d/73-myproject.sh ~/.local/share/MiniServer/updates.d/
This is a one-time step that wires the sibling into every future update
run on the machine.
Optional: only update when installed
Instead of running code at the top level, a hook can define run_update and,
optionally, is_installed. update calls run_update only when is_installed
is missing or returns true, so the hook is skipped on machines where the project
isn't installed. Return non-zero from run_update to report a failure:
#!/bin/bash
is_installed() {
[ -d "$HOME/.local/share/myproject" ]
}
run_update() {
echo "Updating MyProject..."
git -C "$HOME/.local/share/myproject" pull --ff-only
}
Available Helpers
Hooks don't inherit functions from earlier hooks, so source utils.sh for its
helpers. Do this before defining your own is_installed, because utils.sh has
a pacman-specific function with the same name:
source "$MINISERVER/utils.sh"
# Install pacman packages (skips already-installed ones)
install_packages "${MY_PACMAN_PACKAGES[@]}"
# Remove packages
remove_packages "${MY_REMOVE_PACKAGES[@]}"
Checklist for Sibling Project Authors
- Create
updates.d/7X-<project>.shin the sibling repo - Hook script echoes its name so progress is visible during
update - Install step copies the hook to
~/.local/share/MiniServer/updates.d/ - Numeric prefix is an unused number in 73–79
- Script is idempotent — safe to run multiple times without side effects