From 4b2241235eb462a1a68c8d8cb8a6019b7417409a Mon Sep 17 00:00:00 2001 From: Madhura68 Date: Wed, 6 May 2026 01:30:22 +0200 Subject: [PATCH] =?UTF-8?q?feat(deploy):=20bin/deploy-to-nas.sh=20voor=20?= =?UTF-8?q?=C3=A9=C3=A9n-commando=20redeploy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lost terugkerende pijn op: na cross-build op Mac vergeet je makkelijk de .env mee te nemen of vanuit de juiste directory te starten, met "FAIL: ... is not set" als gevolg in pre-flight. Script doet in volgorde: 1. docker buildx build --platform linux/amd64 --load 2. docker save | gzip → scrum4me-agent-runner-amd64.tar.gz 3. scp tarball + compose + (eerste keer) .env naar NAS 4. ssh: docker load + sanity-check op .env + compose up --force-recreate 5. ssh: docker compose logs -f (Ctrl-C om te stoppen) Bestaande NAS-.env wordt niet overschreven. Eerste deploy patcht de NAS-paden via sed. Sanity-check faalt expliciet als anthropic-, SCRUM4ME_- of DATABASE_URL-vars ontbreken — ipv stille pre-flight-fail. Config via .env.deploy (zit in .gitignore). Voor eerste deploy en volledige procedure: README "Deploy — cross-build" sectie. Co-Authored-By: Claude Opus 4.7 (1M context) --- .env.deploy.example | 16 +++++++ .gitignore | 1 + README.md | 28 +++++++++++ bin/deploy-to-nas.sh | 112 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+) create mode 100644 .env.deploy.example create mode 100755 bin/deploy-to-nas.sh diff --git a/.env.deploy.example b/.env.deploy.example new file mode 100644 index 0000000..814f018 --- /dev/null +++ b/.env.deploy.example @@ -0,0 +1,16 @@ +# .env.deploy — config voor bin/deploy-to-nas.sh +# Kopieer naar .env.deploy en pas aan; staat in .gitignore. + +# SSH-target voor de NAS. Moet werkend zijn met SSH-key (geen password). +NAS_HOST=admin@nas.local + +# Pad op de NAS waar tarball + compose + .env terechtkomen. +# Default: /share/Agent/scrum4me-agent-runner +# NAS_REMOTE_DIR=/share/Agent/scrum4me-agent-runner + +# Build-args (overrides). Standaard: +# MCP_GIT_REF=main — pin een commit in productie indien gewenst +# CLAUDE_CODE_VERSION=latest — pin een claude-code release indien gewenst +# AGENT_UID=1000, AGENT_GID=1000 +# MCP_GIT_REF=main +# CLAUDE_CODE_VERSION=latest diff --git a/.gitignore b/.gitignore index 3f91b6f..8b64b2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Secrets .env +.env.deploy *.env.local # Local dev overrides (niet committen, per ontwikkelaar) diff --git a/README.md b/README.md index 046bf98..825abe7 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,34 @@ docker compose logs -f > mapt deze stack standaard `18080:8080`. Override via > `AGENT_HEALTH_PORT_HOST` in `.env` als je een andere host-poort wilt. +## Snelle redeploy — `bin/deploy-to-nas.sh` + +Voor een **bestaande deploy** die je opnieuw wil bouwen + deployen +(bijvoorbeeld na een merge in `scrum4me-mcp` of een aanpassing aan +`CLAUDE.md`): + +```bash +# Eenmalig: NAS-target instellen +cp .env.deploy.example .env.deploy +vi .env.deploy # zet NAS_HOST=admin@ + +# Daarna: één commando voor de hele cyclus +bin/deploy-to-nas.sh +``` + +Het script doet: + +1. `docker buildx build --platform linux/amd64 --load` +2. `docker save | gzip → scrum4me-agent-runner-amd64.tar.gz` +3. `scp` van tarball + `docker-compose.yml` + (eerste keer) `.env` naar NAS +4. `ssh` op NAS: `docker load` + sanity-check op `.env` + `docker compose up -d --force-recreate` +5. `docker compose logs -f` — lokaal-volgbaar terwijl pre-flight + eerste batch starten + +`.env` op de NAS wordt **niet** overschreven als 'ie er al staat. Bij een +verse NAS-installatie wordt 'ie wél geüpload + ge-sed't (NAS_BASE, +AGENT_UID, etc.). Voor de **eerste deploy** of een schoon volume zie de +volledige procedure hieronder. + ## Deploy — cross-build vanaf Mac (Apple Silicon → amd64-NAS) Alternatief voor de in-place build hierboven. Bouw de image op je Mac voor diff --git a/bin/deploy-to-nas.sh b/bin/deploy-to-nas.sh new file mode 100755 index 0000000..409c9c3 --- /dev/null +++ b/bin/deploy-to-nas.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# deploy-to-nas.sh — Mac → NAS cross-build deploy in één commando. +# +# Voert in volgorde uit: +# 1. docker buildx build (linux/amd64, --load) +# 2. docker save | gzip → scrum4me-agent-runner-amd64.tar.gz +# 3. scp tarball + docker-compose.yml + (eerste keer) .env naar NAS +# 4. ssh: docker load + sed-patch .env paths (eerste keer) + compose up --force-recreate +# 5. ssh: docker compose logs -f voor verificatie +# +# Gebruik: +# bin/deploy-to-nas.sh # gebruikt env vars uit .env.deploy +# NAS_HOST=admin@nas bin/deploy-to-nas.sh +# +# Vereisten: +# - docker buildx geïnstalleerd +# - .env aanwezig in repo-root (deze wordt naar NAS gestuurd op eerste run) +# - .env.deploy met NAS_HOST + NAS_REMOTE_DIR (optioneel — anders prompted) +# - SSH-key access naar de NAS (geen password-prompts) + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$REPO_ROOT" + +# ----- config ----------------------------------------------------------- +if [[ -f .env.deploy ]]; then + # shellcheck disable=SC1091 + source .env.deploy +fi + +: "${NAS_HOST:?NAS_HOST not set — bv. admin@nas.local. Zet 'm in .env.deploy of als env-var.}" +: "${NAS_REMOTE_DIR:=/share/Agent/scrum4me-agent-runner}" +: "${IMAGE_TAG:=scrum4me-agent-runner:local}" +: "${TARBALL:=scrum4me-agent-runner-amd64.tar.gz}" +: "${MCP_GIT_REF:=main}" +: "${CLAUDE_CODE_VERSION:=latest}" +: "${AGENT_UID:=1000}" +: "${AGENT_GID:=1000}" + +log() { echo "[deploy-to-nas] $*"; } + +# ----- pre-flight ------------------------------------------------------- +if [[ ! -f .env ]]; then + log "FAIL: .env ontbreekt in repo-root. Maak 'm aan via: cp .env.example .env" + exit 1 +fi + +# ----- 1. buildx -------------------------------------------------------- +log "1/5 docker buildx build (linux/amd64, MCP_GIT_REF=$MCP_GIT_REF)" +docker buildx build \ + --platform linux/amd64 \ + --build-arg "MCP_GIT_REF=${MCP_GIT_REF}" \ + --build-arg "CLAUDE_CODE_VERSION=${CLAUDE_CODE_VERSION}" \ + --build-arg "AGENT_UID=${AGENT_UID}" \ + --build-arg "AGENT_GID=${AGENT_GID}" \ + -t "$IMAGE_TAG" \ + --load \ + . + +# ----- 2. tarball ------------------------------------------------------- +log "2/5 docker save | gzip → $TARBALL" +docker save "$IMAGE_TAG" | gzip > "$TARBALL" +ls -lh "$TARBALL" + +# ----- 3. scp ----------------------------------------------------------- +log "3/5 scp tarball + compose naar $NAS_HOST:$NAS_REMOTE_DIR" +ssh "$NAS_HOST" "mkdir -p '$NAS_REMOTE_DIR'" + +# Check of er al een .env op de NAS staat. Zo niet: stuur de Mac-versie en +# patch hem. Zo wel: laat 'm met rust (kan NAS-specifiek aangepast zijn). +if ssh "$NAS_HOST" "test -f '$NAS_REMOTE_DIR/.env'" 2>/dev/null; then + log " (.env bestaat al op NAS — niet overschreven)" +else + log " geen .env op NAS, kopiëren + patchen" + scp .env "$NAS_HOST:$NAS_REMOTE_DIR/.env" + ssh "$NAS_HOST" " + cd '$NAS_REMOTE_DIR' + chmod 600 .env + sed -i \\ + -e 's|^NAS_BASE=.*|NAS_BASE=/share/Agent|' \\ + -e 's|^AGENT_BASE=.*|AGENT_BASE=/share/Agent|' \\ + -e 's|^AGENT_PLATFORM=.*|AGENT_PLATFORM=linux/amd64|' \\ + -e 's|^AGENT_UID=.*|AGENT_UID=${AGENT_UID}|' \\ + -e 's|^AGENT_GID=.*|AGENT_GID=${AGENT_GID}|' \\ + -e 's|^AGENT_HEALTH_PORT_HOST=.*|AGENT_HEALTH_PORT_HOST=18080|' \\ + .env + " +fi + +scp "$TARBALL" docker-compose.yml package.json README.md \ + "$NAS_HOST:$NAS_REMOTE_DIR/" + +# ----- 4. load + restart ------------------------------------------------ +log "4/5 docker load + compose up --force-recreate op NAS" +ssh "$NAS_HOST" " + set -eu + source /etc/profile + cd '$NAS_REMOTE_DIR' + + # Sanity: env-vars die check-tokens.sh nodig heeft + grep -qE '^(CLAUDE_CODE_OAUTH_TOKEN|ANTHROPIC_API_KEY)=' .env || { echo 'FAIL: anthropic credential ontbreekt in .env'; exit 1; } + grep -qE '^SCRUM4ME_TOKEN=' .env || { echo 'FAIL: SCRUM4ME_TOKEN ontbreekt in .env'; exit 1; } + grep -qE '^DATABASE_URL=' .env || { echo 'FAIL: DATABASE_URL ontbreekt in .env'; exit 1; } + + gunzip -c '$TARBALL' | docker load + docker compose up -d --force-recreate +" + +# ----- 5. tail logs ------------------------------------------------------ +log "5/5 docker compose logs (Ctrl-C om te stoppen)" +ssh "$NAS_HOST" "cd '$NAS_REMOTE_DIR' && docker compose logs -f --tail=50"