shellcheck / shellcheck (push) Failing after 19s
A sibling's version is its latest git tag (v1.4.0, or v1.4.0+3 for commits after it), or, without tags, its commit's date and short hash, like CoreDesktop's own. The version shown as available is that of the newest commit on the default branch, which is what update installs. sibling list shows each sibling's version. sibling check fetches every sibling at once and shows which have a newer version, and sibling update [name...] updates the named siblings or all of them. 71-siblings.sh now runs sibling update, so update and version share one way of updating. version lists the installed siblings with any newer version and, when run in a terminal, asks for each one whether to update it now. version --check and the update notification still only look at CoreDesktop. The sibling skill docs describe how to release a version by tagging it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
159 lines
6.3 KiB
Markdown
159 lines
6.3 KiB
Markdown
# 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/<name>` 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 <url> # same, without the prompt
|
||
sibling list # installed siblings, their version and URL
|
||
sibling check # which siblings have a newer version
|
||
sibling update [name] # update the named siblings, or all of them
|
||
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`:
|
||
|
||
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 |
|
||
|---------------------------|-----------------------------------------|
|
||
| `09-snapshot.sh` | Snapper snapshot of / (btrfs only) |
|
||
| `10-pacman.sh` | Arch system update |
|
||
| `12-migrations.sh` | One-time migrations from `migrations/` |
|
||
| `15-services.sh` | Enable new systemd services |
|
||
| `20-aur.sh` | AUR packages |
|
||
| `30-flatpak.sh` | Flatpak apps |
|
||
| `40-mise.sh` | languages installed with mise, Rails |
|
||
| `45-tmux.sh` | TPM and tmux plugins |
|
||
| `46-neovim.sh` | Neovim plugins |
|
||
| `50-bitwarden.sh` | Bitwarden SSH agent |
|
||
| `60-pipx.sh` | pipx packages |
|
||
| `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-<project>.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
|