coredesktop-dev now gives its VMs QEMU's standard VGA, which qemu-base has, so the extra package and doctor's check are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
15 KiB
Skill: Building coredesktop-dev, the CoreDesktop Development Sibling
Overview
coredesktop-dev is a CoreDesktop sibling project for people who work on
CoreDesktop itself. Installing it with sibling add <url> gives a machine
the tools to change CoreDesktop safely: the linters and test runner, a
command that runs the checks the way CI should, a way to try a working copy
without touching the installed one, and a guard that keeps the installed copy
updatable.
Follow this document when creating the coredesktop-dev repository from
scratch. It covers what to build, why, and how to test it. It assumes
CoreDesktop is installed at ~/.local/share/coredesktop. The rules for any
sibling are in that checkout's skills/sibling-project-format.md: read it
first. This document only adds what is specific to this project.
Why a sibling and not part of CoreDesktop
- Only contributors need it. Every package in CoreDesktop's
packages.conflands on every user's machine. shellcheck, bats and the Gitea CLI don't belong there. - It changes on its own schedule. Development tooling can change without
a CoreDesktop release, and
updatestill keeps it current on every developer's machine.
Background: the two copies of CoreDesktop
Every problem this project solves comes from one fact. A developer's machine has two copies of CoreDesktop:
| Copy | Path | What it is |
|---|---|---|
| Deployed | ~/.local/share/coredesktop |
The machine's running CoreDesktop. A git clone that update pulls with --ff-only. ~/.local/bin commands, updates.d hooks, configure items and sandbox configs all run from here. |
| Working | e.g. ~/Projects/coredesktop |
Where development happens: branches, uncommitted changes. Nothing runs from here unless you run it yourself. |
Lessons learned the hard way, which the tools below exist to enforce:
- Never edit the deployed copy. Copying working files into it to "try
them" leaves tracked files modified, and the next
updatefails to pull (--ff-onlyrefuses). If it happens,git -C ~/.local/share/coredesktop checkout -- .restores it, but it's easy to forget. - Scripts hard-code the deployed path. Most CoreDesktop scripts set
COREDESKTOP="$HOME/.local/share/coredesktop". To run working-copy code as if it were deployed, point a throwawayHOMEat the working copy with a symlink. Don't swap the real deployed copy. - A throwaway
HOMEdoesn't isolate the session.XDG_RUNTIME_DIR(/run/user/<uid>) is shared with the live GNOME session. A test that rangnome-keyring-daemon --replacewith onlyHOMEchanged found the real keyring daemon through/run/user/<uid>/keyring, killed it, and left the user's keyring locked, which hung Bitwarden. Any test that starts session daemons needs its ownXDG_RUNTIME_DIR(mode 700), noDISPLAYorWAYLAND_DISPLAY, and adbus-run-session. Never use--replace. sudo,yay,pacmanandsystemctlignoreHOME. A throwaway home protects your files, not your system. Code that changes the system has to be tested with fake commands (bats stubs) or on a machine or VM you don't mind changing.- Editors can run in a Flatpak. VSCodium from Flathub, for one, runs its
terminal and any AI agent inside the Flatpak sandbox, where
pacman,systemctl,journalctland the host's/tmpdon't exist. Commands there must go throughflatpak-spawn --host. The tools should notice this and say so, rather than fail in confusing ways. make lintonly lints tracked files. CoreDesktop's Makefile builds its list fromgit ls-files, so a new script isn't linted until it's committed. The checks have to include untracked scripts.- CI runs only shellcheck.
.gitea/workflows/shellcheck.ymlrunsmake lint, not the bats tests, somake checkhas to run locally before pushing.
What to build
Repository layout
coredesktop-dev/
install.sh run once by `sibling add`
update.sh run on every `update`
bin/coredesktop-dev the command (symlinked into ~/.local/bin)
hooks/pre-push git hook for the working copy (opt-in)
lib/ one file per subcommand, plus common.sh and vm.sh
vm/ provision.sh and cloud-init user-data for the smoketest VM
tests/ bats tests for this project
Makefile `make check`: shellcheck + bats for this repo
.shellcheckrc copy CoreDesktop's
README.md install with: sibling add <url>
Packages
Installed by update.sh with CoreDesktop's install_packages, so packages
added later reach existing machines on their next update. All are in Arch's
extra repo:
| Package | Why |
|---|---|
shellcheck |
make lint |
bats |
make test (CoreDesktop's tests use plain bats, no helper libraries) |
tea |
Gitea's CLI, for pull requests on gitea.federated.computer |
qemu-base |
The smoketest's virtual machines |
libisoburn |
xorriso, to make the smoketest VM's cloud-init disk |
make, git and jq already come with CoreDesktop. Don't add shfmt until
CoreDesktop decides on a formatting style; it has no config for it today.
Settings
~/.config/coredesktop-dev/config, a shell file install.sh writes and every
other script sources:
WORKING_COPY=/home/<user>/Projects/coredesktop
Write it outside the repo: siblings must never write tracked files.
install.sh
- Source
~/.local/share/coredesktop/utils.sh. Stop with a clear message if it's missing (CoreDesktop isn't installed). - Ask where the working copy is, defaulting to
~/Projects/coredesktop.- If that folder is a git clone of CoreDesktop, use it. Check that its
originpoints at a CoreDesktop repository. - If it doesn't exist, offer to clone it there from the deployed copy's
originURL (git -C ~/.local/share/coredesktop remote get-url origin). - Never use the deployed copy itself as the working copy. Refuse if the
answer resolves to
~/.local/share/coredesktop.
- If that folder is a git clone of CoreDesktop, use it. Check that its
- Save the answer in the settings file.
- Run
update.sh, which installs the packages and the command. - Offer to install the pre-push hook (see
hooksbelow). Default no. - Run
coredesktop-dev doctorand show the result.
It must be safe to re-run: re-running sibling add is how users repair an
install. If the settings file exists, show the current working copy as the
default answer.
update.sh
No prompts. It prints what it does and exits non-zero only on real failure:
install_packages shellcheck bats tea qemu-base libisoburn- Make
~/.local/bin/coredesktop-deva symlink tobin/coredesktop-devin this repo, so each pull updates the command without copying. - If the pre-push hook is installed in the working copy, refresh it from
hooks/pre-push(compare the files, copy only if different).
The coredesktop-dev command
Subcommands, each with --help. With no subcommand, it prints usage.
coredesktop-dev doctor
Read-only. It reports each of these as ok or a problem, with the fix:
- shellcheck, bats and tea are installed.
- The working copy from the settings file exists and is a CoreDesktop clone.
- The deployed copy has no local changes (
git status --porcelain --untracked-files=nois empty). If it has some, list them and point tocoredesktop-dev deployed restore. - The deployed copy can fast-forward to its upstream, or is already there.
- Whether it is running inside a Flatpak (
/.flatpak-infoexists, or$FLATPAK_IDis set). If it is, say that host commands needflatpak-spawn --host. If the subcommand that was asked for needs the host (anything that runsmake checkwith the host's tools), re-run itself on the host throughflatpak-spawn --hostinstead of failing. - Whether the pre-push hook is installed.
Exit 0 if everything is ok, 1 otherwise.
coredesktop-dev check [--fast]
Runs the checks on the working copy, from its root:
make check(shellcheck on tracked scripts, then bats).- shellcheck on untracked scripts too, with the same arguments the
Makefile uses (
--severity=warning, the repo's.shellcheckrc). The files aregit ls-files --others --exclude-standardwhose first line is a bash or sh shebang, plus untracked*.shfiles. That closes the lint gap in lesson 6. --fastruns only the shellcheck steps.
Exit non-zero if anything fails. Print a one-line summary at the end, e.g.
shellcheck: ok (91 files, 6 untracked) bats: ok (85 tests).
coredesktop-dev try [command...]
Runs working-copy code as if it were the deployed CoreDesktop, without changing the deployed copy or the real home folder:
- Make a temporary directory (
mktemp -d) holding ahome/and aruntime/folder (mode 700). - In the temporary home, symlink
.local/share/coredesktopto the working copy and put.local/binat the front ofPATH. Link.local/bin/{update,refresh,sibling,version,configure,audit}to the working copy's scripts of the same names, as CoreDesktop's installer installs them. - Set
HOMEand theXDG_*_HOMEvariables to the temporary home, andXDG_RUNTIME_DIRtoruntime/. UnsetDISPLAY,WAYLAND_DISPLAY,DBUS_SESSION_BUS_ADDRESSandSSH_AUTH_SOCK. - Run the command, or an interactive
bashif none was given, underdbus-run-session, so anything that needs a session bus gets a private one. - Before starting, print a warning:
sudo,yay,pacmanandsystemctlstill change this machine (lesson 4). - Remove the temporary directory afterwards, unless
--keepwas given; then print its path.
Example: coredesktop-dev try configure --list shows what the working copy's
configure would list.
coredesktop-dev deployed [status|restore]
status(the default): the deployed copy's branch, commit, how far behind upstream it is, and any local changes.restore: showgit diff --statof the local changes, ask for confirmation, thengit checkout -- .in the deployed copy. Untracked files are listed but left alone. Nevergit cleanorgit reset --hardthere.
coredesktop-dev hooks [install|remove]
Installs hooks/pre-push into the working copy's .git/hooks/pre-push. The
.git folder isn't tracked, so this is allowed. The hook runs
coredesktop-dev check and blocks the push if it fails. git push --no-verify skips it, and the hook's failure message says so. Refuse to
overwrite a pre-push hook that this project didn't write: write a marker
comment into the hook and check for it.
coredesktop-dev smoketest
Runs CoreDesktop's end-to-end scenarios on QEMU virtual machines. The
scenarios, the fingerprint and health scripts, and the helpers they use live
in CoreDesktop's smoketest/ folder, because they change with the code they
test; read its README for the contract between them and this driver. This
project only provides the VMs: vm_exec, vm_put, vm_reboot, VM_GIT_SSH
and VM_GIT_URL, then calls smoketest_prepare and the scenario.
list,run [--tier pr|all] [--ref REF] [--keep] [scenario...],images [base|installed [REF]],shell [base|installed],clean [--all].- Images, the shared pacman cache, the SSH key and run logs live in
~/.cache/coredesktop-dev/smoketest, never in either copy of CoreDesktop. - base is Arch's cloud image plus archinstall's GNOME desktop profile
(
vm/provision.sh), with automatic login and passwordless sudo fortester. installed is base afterinstall.shat the commit the branch started from. Each scenario runs on a throwaway qcow2 overlay of one of them. - Commands in the VM run in an interactive bash with the GNOME session's environment, as they would in GNOME Terminal.
What not to do
- Don't copy hooks into CoreDesktop's
updates.d/: useupdate.sh, as the sibling format says. Using both updates the project twice. - Don't change CoreDesktop's files from this project, including the working copy's tracked files. Suggest such changes to CoreDesktop instead (below).
- Don't start, stop or
--replacereal session services (keyring, portals, GNOME Shell) in any command or test.
Changes to suggest to CoreDesktop itself
These belong in CoreDesktop's repository, not in this project. Once they're
made, coredesktop-dev check still works, and step 2 above simply finds
nothing extra to lint.
- Lint untracked scripts in the Makefile. Build
SCRIPTSfromgit ls-files --cached --others --exclude-standard. - Run the bats tests in CI. Add a
make testjob to.gitea/workflows/, installingbatsalongsideshellcheck.
Testing this project
Use the same habits as CoreDesktop's own tests:
- bats with stubs. Copy CoreDesktop's
tests/test_helper.bashpattern: a freshHOMEand abin/of fake commands at the front ofPATHfor each test. Fakepacman,yay,sudo,flatpak-spawnandmake, so no test changes the machine. - Prove the tests catch bugs. After the tests pass, break the code on
purpose once per behavior (e.g. make
deployed restoreskip the confirmation, or makecheckskip untracked files) and confirm a test fails each time. - Test the sibling flow in a throwaway home, as described in
sibling-project-format.md.update.shcallsinstall_packages, which usesyay, so stub it there too, or run it only where installing packages is fine.
Cases worth a test each:
install.shrefuses the deployed copy as the working copy, and is safe to re-run.update.shdoesn't prompt and doesn't re-copy an unchanged hook.doctorreports a dirty deployed copy, and reports Flatpak use.checklints an untracked script and fails on a shellcheck warning in it.trygives the command a privateHOMEandXDG_RUNTIME_DIR, noDISPLAY, and the working copy at~/.local/share/coredesktop, and cleans up afterwards.deployed restoreasks first, restores tracked files, and leaves untracked ones alone.hooks installwon't overwrite another hook.
Checklist
- The repo's README says to install with
sibling add <url> install.shasks for the working copy, refuses the deployed copy, saves~/.config/coredesktop-dev/config, and is safe to re-runupdate.shinstalls shellcheck, bats, tea, QEMU and xorriso, keeps the~/.local/bin/coredesktop-devsymlink, and never promptsdoctor,check,try,deployed,hooksandsmoketestwork and have--helpchecklints untracked scripts in the working copytryisolatesHOME,XDG_*andXDG_RUNTIME_DIR, unsets the display and session variables, usesdbus-run-session, and warns that sudo, yay, pacman and systemctl still reach the real system- Nothing writes to the deployed copy except
deployed restore, after confirmation - The project's own
make checkpasses: shellcheck plus bats tests with stubs, and the tests catch deliberately broken code - The two suggested CoreDesktop changes are raised with CoreDesktop