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>
252 lines
9.6 KiB
Markdown
252 lines
9.6 KiB
Markdown
# Skill: Formatting a Repository as a CoreDesktop Sibling Project
|
|
|
|
## Overview
|
|
|
|
A sibling project is a git repository that builds on CoreDesktop. Users install
|
|
one with CoreDesktop's `sibling` command, and from then on CoreDesktop's
|
|
`update` command keeps it up to date.
|
|
|
|
Follow this document when you are working in a sibling repository and need it
|
|
to install and update correctly under CoreDesktop. A repository needs no
|
|
special files to be installable. It becomes useful by adding two optional
|
|
scripts at its top level:
|
|
|
|
| File | When it runs |
|
|
|--------------|--------------------------------------------------------------|
|
|
| `install.sh` | Once, when the user runs `sibling add`, and again if they re-run it |
|
|
| `update.sh` | On every `update`, right after the repo has been pulled |
|
|
|
|
If you want full control over how the project updates, see
|
|
`sibling-project-updates.md` (custom `updates.d` hooks) instead. Use one
|
|
approach or the other, not both, or the project updates twice.
|
|
|
|
## What CoreDesktop Does
|
|
|
|
### `sibling add <url>` (or `sibling`, which asks for the URL)
|
|
|
|
1. Works out the project name from the last part of the URL, dropping `.git`:
|
|
`git@gitea.federated.computer:david/my-tools.git` → `my-tools`.
|
|
2. Clones the repo into `~/.local/share/siblings/<name>`, or, if that folder
|
|
already holds a clone of the same URL, runs `git pull --ff-only` there.
|
|
3. If `install.sh` exists, runs `bash install.sh` with the repo folder as the
|
|
working directory. Its input and output are the user's terminal.
|
|
4. Reports success only if every step above succeeded.
|
|
|
|
### `update`
|
|
|
|
CoreDesktop runs its own update hooks in order (`10-pacman.sh` … `70-claude.sh`).
|
|
Next, `updates.d/71-siblings.sh` runs `sibling update`, which takes each
|
|
sibling in alphabetical order and:
|
|
|
|
1. Runs `git pull --ff-only --quiet` in its folder.
|
|
2. If the pull succeeded and `update.sh` exists, runs `bash update.sh` with the
|
|
repo folder as the working directory.
|
|
|
|
A failed pull or a non-zero exit from `update.sh` is printed and listed in
|
|
`update`'s "Done, with errors in: …" summary. Other siblings still update.
|
|
CoreDesktop's end-of-run hooks (GNOME settings, firmware, cleanup, reboot
|
|
check) run after all siblings.
|
|
|
|
### `sibling update [name...]`
|
|
|
|
Pulls and runs `update.sh` for the named siblings, or for all of them, exactly
|
|
as `update` does, and prints each one's new version. `version` uses it when
|
|
the user agrees to update a sibling that has a newer version.
|
|
|
|
### `sibling remove <name>`
|
|
|
|
This only deletes `~/.local/share/siblings/<name>`. CoreDesktop has no uninstall
|
|
step for siblings, so anything `install.sh` set up elsewhere stays.
|
|
|
|
## Rules for the Repository
|
|
|
|
### Layout
|
|
|
|
- Put `install.sh` and `update.sh` at the top level of the repo. Other names or
|
|
locations are ignored.
|
|
- Both scripts are run with `bash`, so they don't need to be executable, but a
|
|
`#!/bin/bash` line keeps them runnable by hand.
|
|
- Locate files relative to the script, not to a hard-coded path. The user may
|
|
also clone the repo somewhere else and run `install.sh` by hand.
|
|
```bash
|
|
SELF="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
```
|
|
|
|
### Environment
|
|
|
|
- The scripts run as separate `bash` processes, so they inherit **no**
|
|
variables or functions from CoreDesktop. In particular `$COREDESKTOP` is
|
|
**not** set. Use the path directly:
|
|
```bash
|
|
COREDESKTOP="$HOME/.local/share/coredesktop"
|
|
```
|
|
- CoreDesktop's helpers are available by sourcing `utils.sh`:
|
|
|
|
| Function | What it does |
|
|
|--------------------------|-----------------------------------------------------|
|
|
| `install_packages ...` | Installs pacman/AUR packages via `yay`, skipping installed ones |
|
|
| `remove_packages ...` | Removes installed pacman/AUR packages |
|
|
| `remove_flatpaks ...` | Uninstalls flatpaks that are installed |
|
|
| `enable_services ...` | Enables systemd services not yet enabled |
|
|
| `install_script src dst` | Copies a file atomically and makes it executable |
|
|
| `is_installed pkg` | True if a pacman package is installed |
|
|
|
|
- `sudo` prompts for a password as normal. CoreDesktop does not grant
|
|
passwordless sudo to siblings.
|
|
|
|
### `install.sh`
|
|
|
|
- **Exit non-zero on failure.** `set -e` near the top is the simplest way.
|
|
`sibling` then tells the user it failed and how to retry.
|
|
- **Make it safe to re-run.** Re-running `sibling add` is how users repair an
|
|
install, so skip work that is already done instead of failing on it.
|
|
- It may prompt the user, since it runs in their terminal.
|
|
- Install commands into `~/.local/bin` (already on `PATH` under CoreDesktop)
|
|
with `install_script`.
|
|
|
|
### `update.sh`
|
|
|
|
- **Make it idempotent.** It runs on every `update`, usually with nothing to do.
|
|
- **Don't prompt.** Users expect `update` to run through without questions,
|
|
apart from `sudo` passwords.
|
|
- **Exit non-zero only for real failures**, so that `update`'s error summary
|
|
stays meaningful.
|
|
- **Print what it's doing.** CoreDesktop prints only the project's name before
|
|
it runs.
|
|
- The repo has already been pulled, so `update.sh` always runs its newest
|
|
version. Use it to apply changes made since the last update: new packages,
|
|
updated commands in `~/.local/bin`, config migrations.
|
|
- It's fine to omit `update.sh` if pulling the repo is enough, for example when
|
|
`~/.local/bin` holds symlinks into the repo.
|
|
|
|
### Keep pulls working
|
|
|
|
`update` pulls with `--ff-only` and never merges or resets. A pull fails if:
|
|
|
|
- **History on the default branch is rewritten** (force-pushed). Never do this.
|
|
- **Tracked files are edited in place** in the clone and a new commit touches
|
|
them. Scripts must not write into tracked files. Write generated files,
|
|
caches and per-machine state outside the repo (e.g. `~/.config/<name>/`), or
|
|
into paths listed in `.gitignore`.
|
|
|
|
### Versions
|
|
|
|
CoreDesktop shows a sibling's version in `sibling list`, `sibling check` and
|
|
`version`, and works it out from git, so there is no version file to keep:
|
|
|
|
- **With tags:** the latest tag reachable from the commit, e.g. `v1.4.0`, or
|
|
`v1.4.0+3` three commits after it. To release a version, tag the commit on
|
|
the default branch and push the tag:
|
|
```bash
|
|
git tag v1.5.0 && git push origin v1.5.0
|
|
```
|
|
- **Without tags:** the commit's date and short hash, e.g.
|
|
`2026.11.02 (a1b2c3d)`, the way CoreDesktop versions itself.
|
|
|
|
`update` always installs the newest commit on the default branch, tagged or
|
|
not. So the version offered as available is that commit's (`v1.4.0+5` if
|
|
nothing since `v1.4.0` has been tagged). Tag a version when it's ready, rather
|
|
than holding commits back for a release.
|
|
|
|
Annotated and lightweight tags both work, but never move or delete a pushed
|
|
tag: the user's clone keeps the old one.
|
|
|
|
### Authentication
|
|
|
|
`sibling` clones with plain `git`. A private repo works only if the user can
|
|
already clone it (SSH key, credential helper, or a password typed at the
|
|
prompt). Every later `update` needs the same access.
|
|
|
|
## Templates
|
|
|
|
Share the install logic between the two scripts so that packages or commands
|
|
added later reach existing machines through `update`, not just new installs.
|
|
|
|
`install.sh`:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
#
|
|
# Purpose : Install <project> (run by CoreDesktop's `sibling add`)
|
|
|
|
set -e
|
|
|
|
SELF="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
COREDESKTOP="$HOME/.local/share/coredesktop"
|
|
source "$COREDESKTOP/utils.sh"
|
|
|
|
# One-time setup: prompts, accounts, initial config...
|
|
mkdir -p "$HOME/.config/<project>"
|
|
|
|
# Everything that update.sh also needs to keep current
|
|
bash "$SELF/update.sh"
|
|
|
|
echo "<project> installed."
|
|
```
|
|
|
|
`update.sh`:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
#
|
|
# Purpose : Keep <project> current (run by CoreDesktop's `update`)
|
|
|
|
set -e
|
|
|
|
SELF="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
COREDESKTOP="$HOME/.local/share/coredesktop"
|
|
source "$COREDESKTOP/utils.sh"
|
|
|
|
PACKAGES=(
|
|
jq
|
|
ripgrep
|
|
)
|
|
install_packages "${PACKAGES[@]}"
|
|
|
|
mkdir -p "$HOME/.local/bin"
|
|
install_script "$SELF/bin/<project>" "$HOME/.local/bin/<project>"
|
|
```
|
|
|
|
## Testing
|
|
|
|
Test in a throwaway home folder so nothing touches the real one. Point
|
|
`sibling` at a local clone; it accepts any URL or path that `git clone` does.
|
|
|
|
```bash
|
|
export HOME="$(mktemp -d)"
|
|
mkdir -p "$HOME/.local/share"
|
|
ln -s /path/to/real/coredesktop "$HOME/.local/share/coredesktop"
|
|
|
|
# Install
|
|
bash "$HOME/.local/share/coredesktop/sibling" add /path/to/sibling-repo
|
|
|
|
# Commit a change in /path/to/sibling-repo, check that it's seen as newer,
|
|
# then update the way `update` does
|
|
bash "$HOME/.local/share/coredesktop/sibling" check
|
|
bash "$HOME/.local/share/coredesktop/sibling" update
|
|
|
|
# Re-run the install to check it is safe to repeat
|
|
bash "$HOME/.local/share/coredesktop/sibling" add /path/to/sibling-repo
|
|
```
|
|
|
|
Scripts that call `sudo`, `yay` or `systemctl` still change the real system
|
|
even with a temporary `HOME`. Test those on a machine or VM you don't mind
|
|
changing.
|
|
|
|
## Checklist
|
|
|
|
- [ ] The repo can be cloned by URL by the people who will install it
|
|
- [ ] `install.sh` (optional) is at the top level, exits non-zero on failure,
|
|
and is safe to re-run
|
|
- [ ] `update.sh` (optional) is at the top level, idempotent, doesn't prompt,
|
|
and prints what it's doing
|
|
- [ ] Scripts find their own files relative to the script and set
|
|
`COREDESKTOP="$HOME/.local/share/coredesktop"` themselves
|
|
- [ ] Nothing writes to tracked files in the clone, and the default branch is
|
|
never force-pushed
|
|
- [ ] Releases are tagged on the default branch (optional; without tags the
|
|
version is the commit's date and hash), and pushed tags are never moved
|
|
- [ ] The project doesn't also copy a hook into CoreDesktop's `updates.d/`
|
|
- [ ] The README tells users to install with
|
|
`sibling add <url>`
|