Files
PlantAI-dev/tools/migrate_plantai_dev_to_data_ssd.sh
T
2026-08-03 18:42:47 +08:00

201 lines
5.7 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# Migrate PlantAI-dev code from /data_old (HDD) to /data (SSD), while keeping
# input/workspace on /data_old. This script is intentionally conservative:
# - default mode is dry-run
# - old code is renamed, not deleted
# - old code path is replaced by a compatibility symlink after migration
#
# This version does not migrate old input/workspace content. It creates a fresh
# data root at NEW_DATA and links the new code project to NEW_DATA/input and
# NEW_DATA/workspace.
APPLY=0
STOP_SERVICES=0
OLD_CODE="/data_old/home/xia-lab/project_roots/PlantAI-dev"
NEW_CODE="/data/project_roots/PlantAI-dev"
NEW_DATA="/data_old/home/xia-lab/plantai-dev-data"
HOME_LINK="/home/xia-lab/PlantAI-dev"
LEGACY_LINK="/home/xia-lab/wutx/00test/PlantAI-dev"
usage() {
cat <<'EOF'
Usage:
bash tools/migrate_plantai_dev_to_data_ssd.sh [--apply] [--stop-services]
Default is dry-run. Nothing is changed unless --apply is provided.
What --apply does:
1. Optionally stops PlantAI-dev web/Celery processes when --stop-services is used.
2. Copies code from /data_old/home/xia-lab/project_roots/PlantAI-dev to /data/project_roots/PlantAI-dev.
3. Creates fresh input/workspace directories under /data_old/home/xia-lab/plantai-dev-data.
4. Creates input/workspace symlinks inside the new code directory.
5. Renames the old code directory to PlantAI-dev.migrated_<timestamp>.
6. Creates compatibility symlinks at old/common entry paths pointing to the new code directory.
It never deletes the old renamed project.
EOF
}
for arg in "$@"; do
case "$arg" in
--apply)
APPLY=1
;;
--stop-services)
STOP_SERVICES=1
;;
-h|--help)
usage
exit 0
;;
*)
echo "[ERROR] Unknown argument: $arg" >&2
usage
exit 2
;;
esac
done
run() {
if [[ "$APPLY" -eq 1 ]]; then
echo "+ $*"
"$@"
else
echo "[dry-run] $*"
fi
}
require_path() {
local path="$1"
local label="$2"
if [[ ! -e "$path" ]]; then
echo "[ERROR] Missing $label: $path" >&2
exit 1
fi
}
echo "=== PlantAI-dev migration plan ==="
echo "OLD_CODE = $OLD_CODE"
echo "NEW_CODE = $NEW_CODE"
echo "NEW_DATA = $NEW_DATA"
echo "HOME_LINK = $HOME_LINK"
echo "LEGACY_LINK = $LEGACY_LINK"
echo "APPLY = $APPLY"
echo "STOP_SERVICES= $STOP_SERVICES"
echo
require_path "$OLD_CODE" "old code directory"
if [[ ! -d "$OLD_CODE/apps" || ! -d "$OLD_CODE/gfa_core" || ! -d "$OLD_CODE/scripts" ]]; then
echo "[ERROR] OLD_CODE does not look like the full PlantAI-dev code project: $OLD_CODE" >&2
exit 1
fi
echo "=== Disk summary ==="
df -hT /data /data_old / || true
echo
echo "=== Current sizes ==="
du -sh "$OLD_CODE" 2>/dev/null || true
du -sh "$NEW_DATA/input" "$NEW_DATA/workspace" 2>/dev/null || true
echo
if [[ "$STOP_SERVICES" -eq 1 ]]; then
echo "=== Stop PlantAI-dev services ==="
run pkill -f "apps.web.vue3.main:app" || true
run pkill -f "apps/web/vue3/main.py" || true
run pkill -f "celery.*plantai_dev" || true
run pkill -f "celery_app" || true
if [[ "$APPLY" -eq 1 ]]; then
sleep 2
fi
fi
echo "=== Process check ==="
if ps -ef | grep -E "apps.web.vue3.main|apps/web/vue3/main.py|celery.*plantai_dev|celery_app" | grep -v grep; then
if [[ "$STOP_SERVICES" -eq 0 ]]; then
echo "[ERROR] PlantAI-dev-related processes are still running." >&2
echo " Re-run with --stop-services, or stop them manually before --apply." >&2
if [[ "$APPLY" -eq 1 ]]; then
exit 1
fi
fi
else
echo "No matching PlantAI-dev web/Celery processes found."
fi
echo
echo "=== Prepare directories ==="
if [[ "$APPLY" -eq 1 ]]; then
new_code_parent="$(dirname "$NEW_CODE")"
if [[ ! -w "$new_code_parent" ]]; then
echo "[ERROR] $new_code_parent is not writable by $(id -un). Create it and chown it first, for example:" >&2
echo " sudo mkdir -p /data/project_roots" >&2
echo " sudo chown -R $(id -un):$(id -gn) /data/project_roots" >&2
exit 1
fi
fi
run mkdir -p "$(dirname "$NEW_CODE")"
run mkdir -p "$NEW_DATA/input" "$NEW_DATA/workspace"
if [[ -e "$NEW_CODE" && ! -d "$NEW_CODE/apps" ]]; then
echo "[ERROR] NEW_CODE exists but does not look like a PlantAI-dev project: $NEW_CODE" >&2
exit 1
fi
echo "=== Copy code to SSD ==="
run rsync -aHAX --info=progress2 \
--exclude='/input' \
--exclude='/workspace' \
--exclude='/workspace.local_created_during_relink_*' \
"$OLD_CODE"/ "$NEW_CODE"/
echo "=== Data directories ==="
echo "Using data root: $NEW_DATA"
echo "The script will not migrate old input/workspace content."
echo "=== Link new code to HDD data ==="
run ln -sfn "$NEW_DATA/input" "$NEW_CODE/input"
run ln -sfn "$NEW_DATA/workspace" "$NEW_CODE/workspace"
STAMP="$(date +%Y%m%d_%H%M%S)"
RENAMED_OLD="${OLD_CODE}.migrated_${STAMP}"
echo "=== Rename old code project, keep compatibility link ==="
if [[ "$APPLY" -eq 1 ]]; then
if [[ -L "$OLD_CODE" ]]; then
echo "[ERROR] OLD_CODE is already a symlink, refusing to rename: $OLD_CODE" >&2
exit 1
fi
fi
run mv "$OLD_CODE" "$RENAMED_OLD"
run ln -sfn "$NEW_CODE" "$OLD_CODE"
echo "=== Update user-facing links ==="
run ln -sfn "$NEW_CODE" "$HOME_LINK"
run ln -sfn "$NEW_CODE" "$LEGACY_LINK"
echo "=== Final verification ==="
if [[ "$APPLY" -eq 1 ]]; then
echo "New code real path:"
readlink -f "$NEW_CODE"
echo "Data links:"
readlink -f "$NEW_CODE/input"
readlink -f "$NEW_CODE/workspace"
echo "Old code compatibility link:"
ls -ld "$OLD_CODE"
echo "Renamed old code kept at:"
ls -ld "$RENAMED_OLD"
test -d "$NEW_CODE/apps"
test -d "$NEW_CODE/gfa_core"
test -d "$NEW_CODE/scripts"
test -d "$NEW_CODE/input"
test -d "$NEW_CODE/workspace"
echo "[OK] Migration completed. Start services from: $NEW_CODE"
else
echo "[dry-run] No changes made. Re-run with --apply to perform migration."
fi