Show sibling projects' versions, and let version update them
shellcheck / shellcheck (push) Failing after 19s
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>
This commit is contained in:
@@ -253,7 +253,9 @@ sibling
|
||||
|
||||
It asks for the repository's git URL, clones it into ~/.local/share/siblings/, runs the project's "install.sh" (if it has one), and confirms the install. After that, every "update" pulls the latest version of each sibling and runs its "update.sh" (if it has one).
|
||||
|
||||
Use "sibling list" to see what's installed and "sibling remove {name}" to uninstall one.
|
||||
Use "sibling list" to see what's installed, with each project's version, and "sibling remove {name}" to uninstall one. A project's version is its latest release tag (e.g. v1.4.0), or, if it has none, the date and short hash of its latest commit, like CoreDesktop's own.
|
||||
|
||||
"version" lists your sibling projects too, with any newer version. If one has a newer version, it asks whether to update that project now. You can also run "sibling check" to see what's newer, and "sibling update {name}" to update just that one.
|
||||
|
||||
To make your own repository work as a sibling project, see [skills/sibling-project-format.md](skills/sibling-project-format.md).
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
# If it has an install.sh, that runs once when the sibling is added.
|
||||
# Every `update` pulls each sibling and runs its update.sh, if it has one
|
||||
# (see updates.d/71-siblings.sh).
|
||||
#
|
||||
# A sibling's version is its latest git tag, e.g. v1.4.0, or v1.4.0+3 for three
|
||||
# commits after it. A sibling without tags gets CoreDesktop's kind of version:
|
||||
# the commit's date and short hash, e.g. 2026.11.02 (a1b2c3d).
|
||||
|
||||
SIBLINGS="$HOME/.local/share/siblings"
|
||||
|
||||
@@ -14,11 +18,138 @@ usage() {
|
||||
Usage: sibling [command]
|
||||
|
||||
add [url] Install a sibling project from a git repository (default)
|
||||
list Show installed sibling projects
|
||||
list Show installed sibling projects and their versions
|
||||
check Show which sibling projects have a newer version
|
||||
update [name...] Update the named sibling projects, or all of them
|
||||
remove <name> Uninstall a sibling project
|
||||
EOF
|
||||
}
|
||||
|
||||
# Version of the sibling in $1 at commit $2 (HEAD by default)
|
||||
sibling_version() {
|
||||
local dir="$1" rev="${2:-HEAD}" tag ahead
|
||||
if tag="$(git -C "$dir" describe --tags --abbrev=0 "$rev" 2> /dev/null)"; then
|
||||
ahead="$(git -C "$dir" rev-list --count "$tag..$rev" 2> /dev/null)"
|
||||
if [[ "$ahead" =~ ^[0-9]+$ ]] && (( ahead > 0 )); then
|
||||
echo "$tag+$ahead"
|
||||
else
|
||||
echo "$tag"
|
||||
fi
|
||||
else
|
||||
git -C "$dir" log -1 --format='%cd (%h)' --date=format:%Y.%m.%d "$rev"
|
||||
fi
|
||||
}
|
||||
|
||||
# Installed siblings' directories, one per line, in alphabetical order
|
||||
sibling_dirs() {
|
||||
local dir
|
||||
for dir in "$SIBLINGS"/*/; do
|
||||
[ -d "$dir/.git" ] && echo "${dir%/}"
|
||||
done
|
||||
}
|
||||
|
||||
# Fetch the sibling in $1 and print "name<TAB>installed<TAB>available", where
|
||||
# available is the version update would install, empty if it's up to date, or
|
||||
# ? if the sibling's remote couldn't be checked.
|
||||
check_one() {
|
||||
local dir="$1" installed available="" behind
|
||||
installed="$(sibling_version "$dir")"
|
||||
if [ -n "$(git -C "$dir" status --porcelain --untracked-files=no 2> /dev/null)" ]; then
|
||||
installed="$installed, modified"
|
||||
fi
|
||||
if ! GIT_TERMINAL_PROMPT=0 timeout 30 git -C "$dir" fetch --quiet &> /dev/null \
|
||||
|| ! behind="$(git -C "$dir" rev-list --count 'HEAD..@{upstream}' 2> /dev/null)"; then
|
||||
available="?"
|
||||
elif (( behind > 0 )); then
|
||||
available="$(sibling_version "$dir" '@{upstream}')"
|
||||
fi
|
||||
printf '%s\t%s\t%s\n' "$(basename "$dir")" "$installed" "$available"
|
||||
}
|
||||
|
||||
# check_one for every sibling, all at once so a slow remote doesn't hold up the
|
||||
# rest, printed in alphabetical order. Prints nothing if none are installed.
|
||||
check_all() {
|
||||
local dirs dir i=0 tmp
|
||||
mapfile -t dirs < <(sibling_dirs)
|
||||
(( ${#dirs[@]} > 0 )) || return 0
|
||||
tmp="$(mktemp -d)"
|
||||
for dir in "${dirs[@]}"; do
|
||||
check_one "$dir" > "$tmp/$i" &
|
||||
i=$(( i + 1 ))
|
||||
done
|
||||
wait
|
||||
for (( i = 0; i < ${#dirs[@]}; i++ )); do
|
||||
cat "$tmp/$i"
|
||||
done
|
||||
rm -rf "$tmp"
|
||||
}
|
||||
|
||||
# Print check_all's lines as a table. Returns 0 if any sibling has a newer
|
||||
# version, 1 if none does.
|
||||
show_checked() {
|
||||
local name installed available newer=1 width=0 vwidth=0 lines=()
|
||||
mapfile -t lines
|
||||
for line in "${lines[@]}"; do
|
||||
IFS=$'\t' read -r name installed _ <<< "$line"
|
||||
(( ${#name} > width )) && width=${#name}
|
||||
(( ${#installed} > vwidth )) && vwidth=${#installed}
|
||||
done
|
||||
for line in "${lines[@]}"; do
|
||||
IFS=$'\t' read -r name installed available <<< "$line"
|
||||
case "$available" in
|
||||
"") available="up to date" ;;
|
||||
"?") available="couldn't check" ;;
|
||||
*) available="$available is available"; newer=0 ;;
|
||||
esac
|
||||
printf ' %-*s %-*s %s\n' "$width" "$name" "$vwidth" "$installed" "$available"
|
||||
done
|
||||
return $newer
|
||||
}
|
||||
|
||||
check_siblings() {
|
||||
if [ "$1" = "--porcelain" ]; then
|
||||
check_all
|
||||
return
|
||||
fi
|
||||
local checked
|
||||
checked="$(check_all)"
|
||||
if [ -z "$checked" ]; then
|
||||
echo "No sibling projects installed."
|
||||
return 1
|
||||
fi
|
||||
show_checked <<< "$checked"
|
||||
}
|
||||
|
||||
# Pull each named sibling (all of them if none are named) and run its update.sh
|
||||
update_siblings() {
|
||||
local dirs=() dir name failed=0
|
||||
if (( $# == 0 )); then
|
||||
mapfile -t dirs < <(sibling_dirs)
|
||||
fi
|
||||
for name in "$@"; do
|
||||
if [[ -z "$name" || "$name" == */* || "$name" == .* || ! -d "$SIBLINGS/$name/.git" ]]; then
|
||||
echo "No sibling named '$name' is installed."
|
||||
failed=1
|
||||
continue
|
||||
fi
|
||||
dirs+=("$SIBLINGS/$name")
|
||||
done
|
||||
for dir in "${dirs[@]}"; do
|
||||
name="$(basename "$dir")"
|
||||
echo "Updating $name..."
|
||||
if ! git -C "$dir" pull --ff-only --quiet; then
|
||||
echo " Could not pull $name."
|
||||
failed=1
|
||||
continue
|
||||
fi
|
||||
if [ -f "$dir/update.sh" ]; then
|
||||
(cd "$dir" && bash update.sh) || { echo " $name's update.sh failed."; failed=1; continue; }
|
||||
fi
|
||||
echo " $name is at $(sibling_version "$dir")."
|
||||
done
|
||||
return $failed
|
||||
}
|
||||
|
||||
# Derive a directory name from a git URL, e.g.
|
||||
# https://host/user/my-project.git -> my-project
|
||||
sibling_name() {
|
||||
@@ -71,7 +202,7 @@ add_sibling() {
|
||||
|
||||
# Confirm the install
|
||||
if [ -d "$dir/.git" ] && git -C "$dir" rev-parse --verify --quiet HEAD > /dev/null; then
|
||||
echo "Installed $name at $dir ($(git -C "$dir" log -1 --format='%h %s'))."
|
||||
echo "Installed $name $(sibling_version "$dir") at $dir."
|
||||
echo "'update' will keep it up to date."
|
||||
else
|
||||
echo "$name does not look installed correctly at $dir."
|
||||
@@ -80,15 +211,25 @@ add_sibling() {
|
||||
}
|
||||
|
||||
list_siblings() {
|
||||
local dir found=0
|
||||
for dir in "$SIBLINGS"/*/; do
|
||||
[ -d "$dir/.git" ] || continue
|
||||
found=1
|
||||
printf '%-24s %s (%s)\n' "$(basename "$dir")" \
|
||||
"$(git -C "$dir" remote get-url origin 2>/dev/null)" \
|
||||
"$(git -C "$dir" log -1 --format='%h, %cr' 2>/dev/null)"
|
||||
local dirs dir width=0 vwidth=0 versions=()
|
||||
mapfile -t dirs < <(sibling_dirs)
|
||||
if (( ${#dirs[@]} == 0 )); then
|
||||
echo "No sibling projects installed."
|
||||
return
|
||||
fi
|
||||
for dir in "${dirs[@]}"; do
|
||||
versions+=("$(sibling_version "$dir")")
|
||||
dir="$(basename "$dir")"
|
||||
(( ${#dir} > width )) && width=${#dir}
|
||||
done
|
||||
for version in "${versions[@]}"; do
|
||||
(( ${#version} > vwidth )) && vwidth=${#version}
|
||||
done
|
||||
for i in "${!dirs[@]}"; do
|
||||
printf '%-*s %-*s %s
|
||||
' "$width" "$(basename "${dirs[$i]}")" "$vwidth" "${versions[$i]}" \
|
||||
"$(git -C "${dirs[$i]}" remote get-url origin 2> /dev/null)"
|
||||
done
|
||||
[ $found -eq 1 ] || echo "No sibling projects installed."
|
||||
}
|
||||
|
||||
remove_sibling() {
|
||||
@@ -116,6 +257,8 @@ remove_sibling() {
|
||||
case "$1" in
|
||||
""|add) add_sibling "$2" ;;
|
||||
list|ls) list_siblings ;;
|
||||
check) check_siblings "$2" ;;
|
||||
update) shift; update_siblings "$@" ;;
|
||||
remove|rm) remove_sibling "$2" ;;
|
||||
-h|--help|help) usage ;;
|
||||
*) usage; exit 1 ;;
|
||||
|
||||
@@ -35,7 +35,8 @@ approach or the other, not both, or the project updates twice.
|
||||
### `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:
|
||||
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
|
||||
@@ -46,6 +47,12 @@ A failed pull or a non-zero exit from `update.sh` is printed and listed in
|
||||
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
|
||||
@@ -122,6 +129,28 @@ step for siblings, so anything `install.sh` set up elsewhere stays.
|
||||
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
|
||||
@@ -191,9 +220,10 @@ 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 )
|
||||
# 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
|
||||
@@ -214,6 +244,8 @@ changing.
|
||||
`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>`
|
||||
|
||||
@@ -32,7 +32,9 @@ Commands:
|
||||
```bash
|
||||
sibling # asks for the git URL
|
||||
sibling add <url> # same, without the prompt
|
||||
sibling list # installed siblings, their URL and current commit
|
||||
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>
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load test_helper
|
||||
|
||||
# sibling runs against real git repositories: each sibling is a clone of a
|
||||
# repo in $ORIGINS, which a test can add commits and tags to.
|
||||
|
||||
setup() {
|
||||
common_setup
|
||||
export GIT_AUTHOR_NAME=Test GIT_AUTHOR_EMAIL=test@example.com
|
||||
export GIT_COMMITTER_NAME=Test GIT_COMMITTER_EMAIL=test@example.com
|
||||
ORIGINS="$BATS_TEST_TMPDIR/origins"
|
||||
SIBLINGS="$HOME/.local/share/siblings"
|
||||
mkdir -p "$ORIGINS" "$SIBLINGS"
|
||||
}
|
||||
|
||||
# origin NAME: make a repo with one commit, and clone it as a sibling
|
||||
origin() {
|
||||
git init -q -b main "$ORIGINS/$1"
|
||||
commit "$1" first
|
||||
git clone -q "$ORIGINS/$1" "$SIBLINGS/$1"
|
||||
}
|
||||
|
||||
# commit NAME MESSAGE: add a commit to NAME's origin
|
||||
commit() {
|
||||
echo "$2" >> "$ORIGINS/$1/log"
|
||||
git -C "$ORIGINS/$1" add -A
|
||||
git -C "$ORIGINS/$1" commit -qm "$2"
|
||||
}
|
||||
|
||||
@test "sibling list shows a tagged sibling's tag, and an untagged one's date and hash" {
|
||||
origin tagged
|
||||
git -C "$ORIGINS/tagged" tag v1.0.0
|
||||
git -C "$SIBLINGS/tagged" pull -q
|
||||
origin plain
|
||||
|
||||
run bash "$REPO/sibling" list
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"tagged v1.0.0 "*"$ORIGINS/tagged"* ]]
|
||||
[[ "$output" =~ plain\ +[0-9]{4}\.[0-9]{2}\.[0-9]{2}\ \([0-9a-f]+\) ]]
|
||||
}
|
||||
|
||||
@test "sibling list counts the commits since the latest tag" {
|
||||
origin tools
|
||||
git -C "$ORIGINS/tools" tag v1.0.0
|
||||
commit tools second
|
||||
commit tools third
|
||||
git -C "$SIBLINGS/tools" pull -q
|
||||
|
||||
run bash "$REPO/sibling" list
|
||||
[[ "$output" == *"tools v1.0.0+2 "* ]]
|
||||
}
|
||||
|
||||
@test "sibling check shows what's newer, what's up to date, and what it couldn't reach" {
|
||||
origin newer
|
||||
git -C "$ORIGINS/newer" tag v1.0.0
|
||||
git -C "$SIBLINGS/newer" pull -q
|
||||
commit newer second
|
||||
git -C "$ORIGINS/newer" tag v1.1.0
|
||||
origin current
|
||||
origin gone
|
||||
rm -rf "$ORIGINS/gone"
|
||||
|
||||
run bash "$REPO/sibling" check
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"newer v1.0.0"*"v1.1.0 is available"* ]]
|
||||
[[ "$output" == *"current "*"up to date"* ]]
|
||||
[[ "$output" == *"gone "*"couldn't check"* ]]
|
||||
|
||||
run bash "$REPO/sibling" check --porcelain
|
||||
[[ "$output" == *$'newer\tv1.0.0\tv1.1.0'* ]]
|
||||
[[ "$output" == *$'gone\t'*$'\t?'* ]]
|
||||
}
|
||||
|
||||
@test "sibling check exits 1 when nothing is newer" {
|
||||
origin current
|
||||
run bash "$REPO/sibling" check
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"up to date"* ]]
|
||||
}
|
||||
|
||||
@test "sibling update updates only the siblings named" {
|
||||
origin one
|
||||
origin two
|
||||
echo 'echo ran >> "$HOME/one-updated"' > "$ORIGINS/one/update.sh"
|
||||
commit one second
|
||||
git -C "$ORIGINS/one" tag v2.0.0
|
||||
commit two second
|
||||
|
||||
run bash "$REPO/sibling" update one
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"one is at v2.0.0."* ]]
|
||||
[ "$(cat "$HOME/one-updated")" = ran ]
|
||||
[ "$(git -C "$SIBLINGS/two" rev-list --count HEAD)" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "sibling update with no names updates them all, and reports a failing update.sh" {
|
||||
origin one
|
||||
origin two
|
||||
echo 'exit 1' > "$ORIGINS/one/update.sh"
|
||||
commit one second
|
||||
commit two second
|
||||
|
||||
run bash "$REPO/sibling" update
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"one's update.sh failed"* ]]
|
||||
[ "$(git -C "$SIBLINGS/two" rev-list --count HEAD)" -eq 2 ]
|
||||
}
|
||||
|
||||
@test "sibling update refuses a sibling that isn't installed" {
|
||||
run bash "$REPO/sibling" update nope
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"No sibling named 'nope' is installed."* ]]
|
||||
}
|
||||
+47
-3
@@ -4,15 +4,19 @@ load test_helper
|
||||
|
||||
# version runs from a fake checkout with fake git, uname, pacman and
|
||||
# checkupdates. The running kernel's modules never exist here, so the kernel
|
||||
# line always also says a newer one is installed.
|
||||
# line always also says a newer one is installed. Sibling projects are real git
|
||||
# repositories: only git commands on the fake checkout are faked.
|
||||
|
||||
setup() {
|
||||
common_setup
|
||||
CD="$HOME/.local/share/coredesktop"
|
||||
mkdir -p "$CD"
|
||||
cp "$REPO/version" "$REPO/utils.sh" "$CD/"
|
||||
cp "$REPO/version" "$REPO/utils.sh" "$REPO/sibling" "$CD/"
|
||||
|
||||
stub git 'case "$3" in
|
||||
export CD REAL_GIT
|
||||
REAL_GIT="$(command -v git)"
|
||||
stub git '[ "$2" = "$CD" ] || exec "$REAL_GIT" "$@"
|
||||
case "$3" in
|
||||
rev-parse) exit 0 ;;
|
||||
log) echo "2026.10.31 (6bc4fdc)" ;;
|
||||
fetch) exit 1 ;;
|
||||
@@ -89,3 +93,43 @@ setup() {
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != *"mirror"* ]]
|
||||
}
|
||||
|
||||
# sibling_repo NAME: a sibling at v1.0.0 whose origin has a newer v1.1.0
|
||||
sibling_repo() {
|
||||
export GIT_AUTHOR_NAME=Test GIT_AUTHOR_EMAIL=test@example.com
|
||||
export GIT_COMMITTER_NAME=Test GIT_COMMITTER_EMAIL=test@example.com
|
||||
local origin="$BATS_TEST_TMPDIR/origins/$1"
|
||||
git init -q -b main "$origin"
|
||||
echo 'echo "$PWD" >> "$HOME/updated"' > "$origin/update.sh"
|
||||
git -C "$origin" add -A
|
||||
git -C "$origin" commit -qm first
|
||||
git -C "$origin" tag v1.0.0
|
||||
git clone -q "$origin" "$HOME/.local/share/siblings/$1"
|
||||
git -C "$origin" commit -q --allow-empty -m second
|
||||
git -C "$origin" tag v1.1.0
|
||||
}
|
||||
|
||||
@test "version leaves out sibling projects when none are installed" {
|
||||
run bash "$CD/version"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != *"Sibling"* ]]
|
||||
}
|
||||
|
||||
@test "version lists sibling projects, and doesn't ask to update without a terminal" {
|
||||
sibling_repo tools
|
||||
run bash "$CD/version" < /dev/null
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Sibling projects"$'\n'" tools v1.0.0, v1.1.0 is available"* ]]
|
||||
[[ "$output" != *"Update tools"* ]]
|
||||
[ ! -f "$HOME/updated" ]
|
||||
}
|
||||
|
||||
@test "version updates the sibling projects the user says yes to" {
|
||||
sibling_repo keep
|
||||
sibling_repo tools
|
||||
run script -qec "bash '$CD/version'" /dev/null <<< $'n\ny'
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Update keep to v1.1.0? [y/N]"* ]]
|
||||
[[ "$output" == *"tools is at v1.1.0."* ]]
|
||||
[ "$(cat "$HOME/updated")" = "$HOME/.local/share/siblings/tools" ]
|
||||
}
|
||||
|
||||
@@ -8,20 +8,5 @@ is_installed() {
|
||||
}
|
||||
|
||||
run_update() {
|
||||
echo "Updating sibling projects..."
|
||||
local dir name failed=0
|
||||
for dir in "$SIBLINGS"/*/; do
|
||||
[ -d "$dir/.git" ] || continue
|
||||
name="$(basename "$dir")"
|
||||
echo " $name"
|
||||
if ! git -C "$dir" pull --ff-only --quiet; then
|
||||
echo " Could not pull $name."
|
||||
failed=1
|
||||
continue
|
||||
fi
|
||||
if [ -f "$dir/update.sh" ]; then
|
||||
(cd "$dir" && bash update.sh) || { echo " $name's update.sh failed."; failed=1; }
|
||||
fi
|
||||
done
|
||||
return $failed
|
||||
bash "$COREDESKTOP/sibling" update
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
# because they come from Arch, not CoreDesktop, along with any newer release of
|
||||
# either that update would install.
|
||||
#
|
||||
# Sibling projects (see sibling) are listed with their versions too. If any has
|
||||
# a newer version, version asks, for each one, whether to update it now.
|
||||
#
|
||||
# version --check prints the newer version and exits 0 if one is available,
|
||||
# exits 1 if this machine is up to date, or 2 if it couldn't check.
|
||||
|
||||
@@ -125,6 +128,29 @@ echo "Kernel $kernel"
|
||||
[ -n "$driver" ] && echo "Video driver $driver"
|
||||
echo "Last update $last_update"
|
||||
|
||||
# Sibling projects, as "name<TAB>installed<TAB>available" lines (see sibling)
|
||||
siblings="$(bash "$COREDESKTOP/sibling" check --porcelain 2> /dev/null)"
|
||||
newer_siblings=()
|
||||
if [ -n "$siblings" ]; then
|
||||
echo
|
||||
echo "Sibling projects"
|
||||
width=0
|
||||
while IFS=$'\t' read -r name _; do
|
||||
(( ${#name} > width )) && width=${#name}
|
||||
done <<< "$siblings"
|
||||
while IFS=$'\t' read -r name installed available; do
|
||||
case "$available" in
|
||||
"") ;;
|
||||
"?") installed="$installed, couldn't check for a newer version" ;;
|
||||
*)
|
||||
installed="$installed, $available is available"
|
||||
newer_siblings+=("$name"$'\t'"$available")
|
||||
;;
|
||||
esac
|
||||
printf ' %-*s %s\n' "$width" "$name" "$installed"
|
||||
done <<< "$siblings"
|
||||
fi
|
||||
|
||||
# Packages newer than the mirror's last sync aren't shown above, or installed
|
||||
if age="$(mirror_age_hours)" && (( age >= MIRROR_STALE_HOURS )); then
|
||||
if (( age >= 48 )); then ago="$(( age / 24 )) days"; else ago="$age hours"; fi
|
||||
@@ -132,3 +158,19 @@ if age="$(mirror_age_hours)" && (( age >= MIRROR_STALE_HOURS )); then
|
||||
echo -e "\e[33mYour package mirror ($(first_mirror)) last synced with Arch $ago ago, so"
|
||||
echo -e "newer packages may be missing. Run update to switch to up-to-date mirrors.\e[0m"
|
||||
fi
|
||||
|
||||
# Offer each newer sibling, but only to someone at the terminal to answer
|
||||
if (( ${#newer_siblings[@]} > 0 )) && [ -t 0 ] && [ -t 1 ]; then
|
||||
echo
|
||||
chosen=()
|
||||
for sibling in "${newer_siblings[@]}"; do
|
||||
IFS=$'\t' read -r name available <<< "$sibling"
|
||||
read -rp "Update $name to $available? [y/N] " response
|
||||
[[ "$response" =~ ^[yY]([eE][sS])?$ ]] && chosen+=("$name")
|
||||
done
|
||||
if (( ${#chosen[@]} > 0 )); then
|
||||
echo
|
||||
bash "$COREDESKTOP/sibling" update "${chosen[@]}"
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user