ZDDC/scripts/migrate-toplevel-peers.sh
ZDDC 3e7aa34e49 feat(scripts): migrate relocates table.yaml/form.yaml configs into .zddc.d/
Adds a tree-wide config-relocation pass to migrate-toplevel-peers.sh: after
the peer move, every <dir>/table.yaml and <dir>/form.yaml is moved into
<dir>/.zddc.d/ (where the server now resolves specs from; the legacy root
still works, so it's a declutter). find|while-read handles directory names
with spaces; skips files already under .zddc.d/ and existing destinations;
honored by --dry-run. Idempotent (verified: dry-run → real → re-run skips).

(No server code writes these configs — they're operator/test-created — so
there are no writers to repoint.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 10:24:03 -05:00

175 lines
4.8 KiB
Bash
Executable file

#!/bin/sh
# migrate-toplevel-peers.sh — migrate a ZDDC root from the old
# "everything under archive/<party>/" layout to the flat top-level
# party-peer layout.
#
# Per project (a top-level dir containing archive/), for each
# archive/<party>/:
# - move archive/<party>/{incoming,working,staging,reviewing,mdl,rsk}
# → <project>/<slot>/<party>/ (the workspace/register peers)
# - move archive/<party>/ssr.yaml → <project>/ssr/<party>.yaml
# (this establishes the party registry — a party exists iff its
# ssr/<party>.yaml exists)
# - if a party has no ssr.yaml, synthesize a minimal ssr/<party>.yaml
# so it stays registered (else its workspaces can't be recreated)
# - LEAVE archive/<party>/{received,issued} in place (the WORM record)
#
# Then, tree-wide, relocate any table.yaml / form.yaml config out of a
# directory root and into that directory's .zddc.d/ reserve (where the
# server now resolves specs from; the legacy root location still works, so
# this is a declutter, not a hard requirement).
#
# Per-folder .zddc files travel with their directory (the whole slot dir
# is moved). Idempotent: already-migrated paths are skipped. Run with the
# server stopped (or accept it's a plain filesystem move).
#
# Usage:
# migrate-toplevel-peers.sh [--dry-run] <ZDDC_ROOT>
#
# --dry-run prints what would happen and changes nothing.
set -eu
DRY=0
ROOT=""
for arg in "$@"; do
case "$arg" in
--dry-run) DRY=1 ;;
-h|--help)
sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'
exit 0
;;
-*)
echo "unknown flag: $arg" >&2
exit 2
;;
*) ROOT="$arg" ;;
esac
done
if [ -z "$ROOT" ] || [ ! -d "$ROOT" ]; then
echo "usage: $0 [--dry-run] <ZDDC_ROOT>" >&2
exit 2
fi
WORKSPACE_SLOTS="incoming working staging reviewing mdl rsk"
moved=0
synth=0
skipped=0
say() { printf '%s\n' "$*"; }
act() {
# act <description> <command...>
desc=$1
shift
if [ "$DRY" -eq 1 ]; then
say "DRY $desc"
else
say " -> $desc"
"$@"
fi
}
# move_dir SRC DST — move a directory, creating DST's parent. Skips when
# SRC is absent (nothing to do) or DST already exists (already migrated).
move_dir() {
src=$1
dst=$2
[ -d "$src" ] || return 0
if [ -e "$dst" ]; then
say " .. skip (dest exists): $dst"
skipped=$((skipped + 1))
return 0
fi
parent=$(dirname "$dst")
act "mkdir -p $parent" mkdir -p "$parent"
act "mv $src -> $dst" mv "$src" "$dst"
moved=$((moved + 1))
}
# move_file SRC DST — same shape, for the ssr.yaml registry row.
move_file() {
src=$1
dst=$2
[ -f "$src" ] || return 0
if [ -e "$dst" ]; then
say " .. skip (dest exists): $dst"
skipped=$((skipped + 1))
return 0
fi
parent=$(dirname "$dst")
act "mkdir -p $parent" mkdir -p "$parent"
act "mv $src -> $dst" mv "$src" "$dst"
moved=$((moved + 1))
}
synth_registry() {
dst=$1
[ -e "$dst" ] && return 0
parent=$(dirname "$dst")
act "mkdir -p $parent" mkdir -p "$parent"
if [ "$DRY" -eq 1 ]; then
say "DRY synthesize $dst (kind: SSR)"
else
say " -> synthesize $dst (kind: SSR)"
printf 'kind: SSR\n' >"$dst"
fi
synth=$((synth + 1))
}
# relocate_configs — move every <dir>/table.yaml and <dir>/form.yaml into
# <dir>/.zddc.d/. Tree-wide; skips files already under a .zddc.d/ and any
# destination that already exists. Uses find | while-read so directory names
# with spaces are handled (counters live in the subshell, so this pass just
# logs its actions rather than feeding the summary).
relocate_configs() {
find "$ROOT" -type f \( -name table.yaml -o -name form.yaml \) 2>/dev/null | while IFS= read -r f; do
case "$f" in
*/.zddc.d/*) continue ;;
esac
d=$(dirname "$f")
base=$(basename "$f")
dst="$d/.zddc.d/$base"
if [ -e "$dst" ]; then
say " .. skip (dest exists): $dst"
continue
fi
act "mkdir -p $d/.zddc.d" mkdir -p "$d/.zddc.d"
act "mv $f -> $dst" mv "$f" "$dst"
done
}
for projectdir in "$ROOT"/*/; do
[ -d "$projectdir/archive" ] || continue
project=$(basename "$projectdir")
case "$project" in .* | _*) continue ;; esac
say "project: $project"
for partydir in "$projectdir"archive/*/; do
[ -d "$partydir" ] || continue
party=$(basename "$partydir")
case "$party" in .* | _*) continue ;; esac
say " party: $party"
# 1. Registry row: archive/<party>/ssr.yaml -> ssr/<party>.yaml
move_file "${partydir}ssr.yaml" "${projectdir}ssr/${party}.yaml"
# 2. Workspace/register slots -> <slot>/<party>/
for slot in $WORKSPACE_SLOTS; do
move_dir "${partydir}${slot}" "${projectdir}${slot}/${party}"
done
# 3. Guarantee a registry entry so the party stays registered.
synth_registry "${projectdir}ssr/${party}.yaml"
done
done
# Tree-wide config relocation (runs over the now-migrated layout).
say ""
say "relocating table.yaml/form.yaml configs into .zddc.d/ …"
relocate_configs
say ""
say "summary: moved=$moved synthesized=$synth skipped=$skipped"
[ "$DRY" -eq 1 ] && say "(dry-run — nothing changed)"
exit 0