# Skill: Supporting the updates.d Update Architecture in a Sibling Project ## Overview There are two ways for a sibling project to plug into CoreDesktop: 1. **The `sibling` command (preferred).** The user runs `sibling` and gives it the project's git URL. The repo is cloned into `~/.local/share/siblings/` and its `install.sh` (if any) is run once. On every `update`, `updates.d/71-siblings.sh` pulls each sibling and runs its `update.sh` (if any). The sibling only needs those two optional scripts. 2. **A custom hook.** CoreDesktop's `update` script sources every `*.sh` file found in `~/.local/share/coredesktop/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: ```bash sibling # asks for the git URL sibling add # same, without the prompt sibling list # installed siblings, their URL and current commit sibling remove # deletes ~/.local/share/siblings/ ``` The rest of this document covers the custom-hook approach. ## How It Works When a user runs `update`: 1. The coredesktop repo at `~/.local/share/coredesktop` is pulled. 2. All `*.sh` files in `updates.d/` are **sourced**, each in its own subshell, in lexicographic order. 3. Each script can see `$COREDESKTOP`, but nothing defined by earlier hooks — source `utils.sh` yourself if you need its helpers. A `cd`, `set -e` or `exit` in one hook can't affect the others. 4. 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 coredesktop hooks use a numeric prefix to control execution order: | File | Purpose | |---------------------------|-----------------------------------------| | `00-rename-migration.sh` | Legacy command renames | | `10-pacman.sh` | Arch system update | | `15-services.sh` | Enable new systemd services | | `20-aur.sh` | AUR packages | | `30-flatpak.sh` | Flatpak apps | | `40-ruby.sh` | mise, Ruby/gems | | `45-tmux.sh` | TPM and tmux plugins | | `46-neovim.sh` | Neovim plugins | | `50-bitwarden.sh` | Bitwarden SSH agent | | `60-pipx.sh` | pipx packages | | `65-migrate.sh` | Migrate backup tool | | `70-claude.sh` | Claude Code setup | | `71-siblings.sh` | Projects installed with `sibling` | | `80-gnome-extensions.sh` | GNOME extensions | | `85-gnome-settings.sh` | GNOME extension settings | | `90-firmware.sh` | Firmware update check | | `95-cleanup.sh` | Flatpak/pacman cache cleanup | | `99-reboot-check.sh` | Reboot-needed notice | Sibling projects should use an unused number from **72–79** so they run after the core updates but before coredesktop's end-of-run hooks (80+). ## What to Add to a Sibling Project ### 1. Create the hook script Add a file like `updates.d/72-myproject.sh` to the sibling repo: ```bash #!/bin/bash echo "Updating MyProject..." git -C "$HOME/.local/share/myproject" pull # Optional: use coredesktop helpers if needed # source "$COREDESKTOP/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 coredesktop's `updates.d/`: ```bash echo "Registering MyProject update hook..." cp updates.d/72-myproject.sh ~/.local/share/coredesktop/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: ```bash #!/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: ```bash source "$COREDESKTOP/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-.sh` in the sibling repo - [ ] Hook script echoes its name so progress is visible during `update` - [ ] Install step copies the hook to `~/.local/share/coredesktop/updates.d/` - [ ] Numeric prefix is an unused number in 72–79 - [ ] Script is idempotent — safe to run multiple times without side effects