#!/usr/bin/env bash # BoardRipper NAS DEV Remote Deploy Script # Runs ON the NAS via SSH (SCP'd by NASdeploy-dev.sh). # Creates a SEPARATE "boardripper-dev" container for testing a branch against # the SAME library as live, WITHOUT touching the live "boardripper" container. # # Key isolation guarantees: # - DEV data dir is /volume1/docker/boardripper-dev/data (SEPARATE from live). # Critical: the v0->v1 PDF-index migration drops legacy pdf tables from # databank.db, so sharing the live data dir would corrupt production. # - DEV replicates EVERY /library* bind mount from the live container, mounted # READ-ONLY, so dev sees the identical library tree (live mounts them rw; # dev forces ro so it can't modify the shared folders). # - NO docker socket mount (dev needs no self-update; safer). # # Arguments: $1 = sudo password set -e DOCKER="/usr/local/bin/docker" PW="$1" LIVE_NAME="boardripper" DEV_NAME="boardripper-dev" DEV_IMAGE="boardripper-dev:latest" DEV_DATA="/volume1/docker/boardripper-dev/data" DEV_PORT="${DEV_PORT:-1234}" DEV_MEM="${DEV_MEM:-1024m}" DEV_POOL="${DEV_POOL:-2}" TARBALL="/tmp/${DEV_NAME}.tar.gz" sdocker() { echo "${PW}" | sudo -S ${DOCKER} "$@" 2>&1 | grep -v '^\[sudo\]' | grep -v '^Password:' || true } # Load the image only if a tarball was shipped; otherwise assume it's already # loaded (lets us re-run with corrected mounts without re-shipping). if [ -f "${TARBALL}" ]; then echo "[NAS-dev] Decompressing image..." gunzip -f "${TARBALL}" echo "[NAS-dev] Loading Docker image..." sdocker load -i "/tmp/${DEV_NAME}.tar" else echo "[NAS-dev] No tarball at ${TARBALL} — using already-loaded ${DEV_IMAGE}." fi # ── Capture the live container's config (for /library* mount discovery) ── # stdout (JSON) → user-owned file; the sudo prompt goes to stderr (suppressed). echo "[NAS-dev] Reading live container config..." echo "${PW}" | sudo -S ${DOCKER} inspect "${LIVE_NAME}" > /tmp/live_inspect.json 2>/dev/null || true # ── Ensure dev data dir exists ── echo "[NAS-dev] Ensuring dev data dir ${DEV_DATA}..." echo "${PW}" | sudo -S mkdir -p "${DEV_DATA}" 2>&1 | grep -v '^\[sudo\]' | grep -v '^Password:' || true # ── Build the full docker-run arg list as NUL-delimited bytes in a FILE ── # (bash variables cannot hold NUL, and library paths contain spaces, so we keep # everything in files and exec via `xargs -0`.) Python replicates every # /library* mount from the live inspect as read-only. echo "[NAS-dev] Building run args (replicating live /library* mounts, read-only)..." python3 - "${DEV_NAME}" "${DEV_IMAGE}" "${DEV_DATA}" "${DEV_PORT}" "${DEV_MEM}" "${DEV_POOL}" > /tmp/dev_run_args <<'PY' import sys, json dev_name, dev_image, dev_data, dev_port, dev_mem, dev_pool = sys.argv[1:7] args = ['run', '-d', '--name', dev_name, '--restart', 'unless-stopped', '--user', '0:0', '--memory', dev_mem, '-p', f'{dev_port}:8080', '-v', f'{dev_data}:/data'] libs = [] try: data = json.load(open('/tmp/live_inspect.json')) if isinstance(data, list): data = data[0] for m in data.get('Mounts', []): dest, src = m.get('Destination', ''), m.get('Source', '') if dest == '/library' or dest.startswith('/library/'): args += ['-v', f'{src}:{dest}:ro'] libs.append(f'{src} -> {dest} (ro)') except Exception as e: sys.stderr.write(f' (inspect parse failed: {e})\n') if not libs: args += ['-v', '/volume1/AL ZEUG/LogiCloud/Schematics-BV-EFI:/library:ro'] sys.stderr.write(' WARNING: no live /library mounts found — using single LogiCloud fallback\n') args += ['-e', 'PORT=8080', '-e', 'LIBRARY_DIR=/library', '-e', f'PDFINDEX_POOL_MAX={dev_pool}', dev_image] sys.stdout.buffer.write(b'\0'.join(a.encode() for a in args)) sys.stderr.write('\n'.join(' ' + l for l in libs) + '\n') PY # ── Recreate the dev container ── echo "[NAS-dev] Stopping old dev container (if any)..." sdocker stop ${DEV_NAME} 2>/dev/null || true sdocker rm ${DEV_NAME} 2>/dev/null || true echo "[NAS-dev] Starting ${DEV_NAME} on port ${DEV_PORT}..." echo "${PW}" | sudo -S bash -c "xargs -0 -a /tmp/dev_run_args ${DOCKER}" 2>&1 \ | grep -v '^\[sudo\]' | grep -v '^Password:' || true rm -f /tmp/dev_run_args /tmp/live_inspect.json echo "[NAS-dev] Pruning dangling images..." sdocker image prune -f 2>/dev/null || true echo "[NAS-dev] Container status:" sdocker ps --filter "name=${DEV_NAME}" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" echo "[NAS-dev] Mounts:" echo "${PW}" | sudo -S ${DOCKER} inspect ${DEV_NAME} 2>/dev/null | python3 -c " import sys, json data = json.load(sys.stdin) if isinstance(data, list): data = data[0] for m in data.get('Mounts', []): mode = 'ro' if not m.get('RW', True) else 'rw' print(f' {m[\"Source\"]} -> {m[\"Destination\"]} ({mode})') " 2>/dev/null || echo " (could not read mounts)" echo "[NAS-dev] Cleaning up..." rm -f "/tmp/${DEV_NAME}.tar" /tmp/deploy-remote-dev.sh echo "[NAS-dev] Done. Dev UI: http://:${DEV_PORT}"