# 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 ` (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/`, 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` 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 remove ` This only deletes `~/.local/share/siblings/`. 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//`), or into paths listed in `.gitignore`. ### 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 (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/" # Everything that update.sh also needs to keep current bash "$SELF/update.sh" echo " installed." ``` `update.sh`: ```bash #!/bin/bash # # Purpose : Keep 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/" "$HOME/.local/bin/" ``` ## 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, then run the update hook the way # `update` does ( source "$HOME/.local/share/coredesktop/updates.d/71-siblings.sh"; is_installed && run_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 - [ ] The project doesn't also copy a hook into CoreDesktop's `updates.d/` - [ ] The README tells users to install with `sibling add `