Files
computer-playbook/scripts/system/worktree/lib.sh
T
kevinveenbirkenbachandClaude Opus 5 8aba198aec feat(worktree): isolated per-branch checkouts sharing one cache stack
Parallel checkouts collided on the compose subnet, host ports and container
names, so a quality run in a worktree died at compose-up. Slot each checkout
via custom.env and layer two overrides on top: the shared cache network in
place of a second cache stack, and the shared .git directory without which
git aborts inside the container.

Both overlays gate on the resource they need, not on the slot number, so a
stray INFINITO_INSTANCE cannot make compose demand a value nobody set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 16:15:59 +02:00

69 lines
2.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# shellcheck shell=bash
#
# Shared helpers for the worktree slot manager. A slot is an integer that
# isolates one checkout from every other: it shifts the compose subnet, the
# host bind IP and the swarm lab subnet, so several worktrees can run their
# stacks side by side. Slot 0 is the primary checkout and owns the shared
# cache stack; every worktree gets the next free slot and joins that cache
# over an external docker network.
#
# Volumes are deliberately not slotted: compose already namespaces them by
# project, and INFINITO_DOCKER_VOLUME names a top-level compose key, so
# renaming it would point the mount at an undeclared volume.
#
# The pins are written to custom.env AND passed to the generator in a cleared
# environment: custom.env keeps them reproducible across later `make dotenv`
# runs, while the explicit env covers branches whose generator predates
# custom.env support. Inheriting the caller's environment would leak the
# invoking worktree's own slot into the new one.
#
# Slot discovery reads custom.env first: a branch whose default.env predates
# INFINITO_INSTANCE never materialises the key into .env, which would make the
# checkout look like slot 0 and hand its number out twice.
#
# Sourced by up.sh and down.sh; not meant to be executed directly.
WORKTREE_DIR_PREFIX="infinito-wt-"
worktree_slug() {
printf '%s' "$1" | sed -e 's#[^a-zA-Z0-9._-]#-#g' -e 's#^[^a-zA-Z0-9]*##' -e 's#[-.]*$##'
}
worktree_path() {
printf '%s/%s%s' "$2" "${WORKTREE_DIR_PREFIX}" "$1"
}
worktree_slot_of() {
local slot=0 file
for file in "$1/custom.env" "$1/.env"; do
if [ -f "${file}" ]; then
slot="$(awk -F= '/^INFINITO_INSTANCE=/{print $2}' "${file}" | tail -n1)"
if [ -n "${slot}" ]; then
break
fi
fi
done
case "${slot}" in
'' | *[!0-9]*) slot=0 ;;
esac
printf '%s' "${slot}"
}
worktree_next_slot() {
local max=0 path slot
while read -r path; do
slot="$(worktree_slot_of "${path}")"
if [ "${slot}" -gt "${max}" ]; then
max="${slot}"
fi
done < <(git worktree list --porcelain | awk '/^worktree /{print $2}')
printf '%s' "$((max + 1))"
}
worktree_cache_network() {
docker inspect infinito-registry-cache \
--format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{"\n"}}{{end}}' 2>/dev/null |
head -n1
}