From 57ebd76070dc6de89e2f3ed77c3c73eeaba04209 Mon Sep 17 00:00:00 2001 From: Kpa-clawbot <259247574+Kpa-clawbot@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:58:22 -0700 Subject: [PATCH] fix: config.json lives in data dir, not bind-mounted as file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the separate config.json file bind mount from both compose files. The data directory mount already covers it, and the Go server searches /app/data/config.json via LoadConfig. - Entrypoint symlinks /app/data/config.json for ingestor compatibility - manage.sh setup creates config in data dir, prompts admin if missing - manage.sh start checks config exists before starting, offers to create - deploy.yml simplified — no more sudo rm or directory cleanup - Backup/restore updated to use data dir path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/deploy.yml | 13 ++-- docker-compose.staging.yml | 1 - docker-compose.yml | 1 - docker/entrypoint-go.sh | 18 +++--- manage.sh | 118 ++++++++++++++++++++++++++++------- 5 files changed, 110 insertions(+), 41 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 71f59f3..d204d04 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -264,8 +264,7 @@ jobs: - name: Deploy staging run: | - # Use docker compose down (not just stop/rm) to properly clean up - # the old container, network, and release memory before starting new one + # Stop old container and release memory docker compose -f "$STAGING_COMPOSE_FILE" -p corescope-staging down --timeout 30 2>/dev/null || true # Wait for container to be fully gone and OS to reclaim memory (3GB limit) @@ -277,14 +276,14 @@ jobs: done sleep 5 # extra pause for OS memory reclaim - # Ensure staging config exists (docker creates a directory if bind mount source missing) + # Ensure staging data dir exists (config.json lives here, no separate file mount) STAGING_DATA="${STAGING_DATA_DIR:-$HOME/meshcore-staging-data}" mkdir -p "$STAGING_DATA" - # Remove directory-masquerading-as-file left by failed docker mount - [ -d "$STAGING_DATA/config.json" ] && rm -rf "$STAGING_DATA/config.json" + + # If no config exists, copy the example (CI doesn't have a real prod config) if [ ! -f "$STAGING_DATA/config.json" ]; then - echo "Staging config missing — copying from repo config.json" - cp config.json "$STAGING_DATA/config.json" 2>/dev/null || cp config.example.json "$STAGING_DATA/config.json" + echo "Staging config missing — copying config.example.json" + cp config.example.json "$STAGING_DATA/config.json" 2>/dev/null || true fi docker compose -f "$STAGING_COMPOSE_FILE" -p corescope-staging up -d staging-go diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml index e2fd19a..f14560a 100644 --- a/docker-compose.staging.yml +++ b/docker-compose.staging.yml @@ -25,7 +25,6 @@ services: - "6060:6060" # pprof server - "6061:6061" # pprof ingestor volumes: - - ${STAGING_DATA_DIR:-~/meshcore-staging-data}/config.json:/app/config.json:ro - ${STAGING_DATA_DIR:-~/meshcore-staging-data}:/app/data - caddy-data-staging-go:/data/caddy environment: diff --git a/docker-compose.yml b/docker-compose.yml index f89de0a..b1269a3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,6 @@ services: - "${PROD_HTTPS_PORT:-443}:443" - "${PROD_MQTT_PORT:-1883}:1883" volumes: - - ./config.json:/app/config.json:ro - ./caddy-config/Caddyfile:/etc/caddy/Caddyfile:ro - ${PROD_DATA_DIR:-~/meshcore-data}:/app/data - caddy-data:/data/caddy diff --git a/docker/entrypoint-go.sh b/docker/entrypoint-go.sh index 36dad08..9a6ce87 100644 --- a/docker/entrypoint-go.sh +++ b/docker/entrypoint-go.sh @@ -1,16 +1,12 @@ #!/bin/sh -# Fix: Docker creates a directory when bind-mounting a non-existent file. -# If config.json is a directory (from a failed mount), remove it and use the example. -if [ -d /app/config.json ]; then - echo "[entrypoint] WARNING: config.json is a directory (broken bind mount) — removing and using example" - rm -rf /app/config.json -fi - -# Copy example config if no config.json exists (not bind-mounted) -if [ ! -f /app/config.json ]; then - echo "[entrypoint] No config.json found, copying from config.example.json" - cp /app/config.example.json /app/config.json +# Config lives in the data directory (bind-mounted from host) +# The Go server already searches /app/data/config.json via LoadConfig +# but the ingestor expects a direct path — symlink for compatibility +if [ -f /app/data/config.json ]; then + ln -sf /app/data/config.json /app/config.json +elif [ ! -f /app/config.json ]; then + echo "[entrypoint] No config.json found in /app/data/ — using built-in defaults" fi # theme.json: check data/ volume (admin-editable on host) diff --git a/manage.sh b/manage.sh index 760275e..6c83475 100755 --- a/manage.sh +++ b/manage.sh @@ -68,10 +68,11 @@ is_done() { [ -f "$STATE_FILE" ] && grep -qx "$1" "$STATE_FILE" 2>/dev/null; # Check config.json for placeholder values check_config_placeholders() { - if [ -f config.json ]; then - if grep -qE 'your-username|your-password|your-secret|example\.com|changeme' config.json 2>/dev/null; then + local cfg="${1:-$PROD_DATA/config.json}" + if [ -f "$cfg" ]; then + if grep -qE 'your-username|your-password|your-secret|example\.com|changeme' "$cfg" 2>/dev/null; then warn "config.json contains placeholder values." - warn "Edit config.json and replace placeholder values before deploying." + warn "Edit ${cfg} and replace placeholder values before deploying." fi fi } @@ -179,19 +180,27 @@ cmd_setup() { # ── Step 2: Config ── step 2 "Configuration" - if [ -f config.json ]; then - log "config.json already exists (not overwriting)." + if [ -f "$PROD_DATA/config.json" ]; then + log "config.json found in data directory." # Sanity check the JSON - if ! python3 -c "import json; json.load(open('config.json'))" 2>/dev/null && \ - ! node -e "JSON.parse(require('fs').readFileSync('config.json'))" 2>/dev/null; then + if ! python3 -c "import json; json.load(open('$PROD_DATA/config.json'))" 2>/dev/null && \ + ! node -e "JSON.parse(require('fs').readFileSync('$PROD_DATA/config.json'))" 2>/dev/null; then err "config.json has invalid JSON. Fix it and re-run setup." exit 1 fi log "config.json is valid JSON." - check_config_placeholders + check_config_placeholders "$PROD_DATA/config.json" + elif [ -f config.json ]; then + # Legacy: config in repo root — move it to data dir + info "Found config.json in repo root — moving to data directory..." + mkdir -p "$PROD_DATA" + cp config.json "$PROD_DATA/config.json" + log "Config moved to ${PROD_DATA}/config.json" + check_config_placeholders "$PROD_DATA/config.json" else - info "Creating config.json from example..." - cp config.example.json config.json + info "Creating config.json in data directory from example..." + mkdir -p "$PROD_DATA" + cp config.example.json "$PROD_DATA/config.json" # Generate a random API key if command -v openssl &> /dev/null; then @@ -201,14 +210,14 @@ cmd_setup() { fi # Replace the placeholder API key if command -v sed &> /dev/null; then - sed -i "s/your-secret-api-key-here/${API_KEY}/" config.json + sed -i "s/your-secret-api-key-here/${API_KEY}/" "$PROD_DATA/config.json" fi log "Created config.json with random API key." - check_config_placeholders + check_config_placeholders "$PROD_DATA/config.json" echo "" - echo " You can customize config.json later (map center, branding, etc)." - echo " Edit with: nano config.json" + echo " Config saved to: ${PROD_DATA}/config.json" + echo " Edit with: nano ${PROD_DATA}/config.json" echo "" fi mark_done "config" @@ -402,10 +411,15 @@ prepare_staging_db() { # Copy config.prod.json → config.staging.json with siteName change prepare_staging_config() { - local prod_config="./config.json" + local prod_config="$PROD_DATA/config.json" local staging_config="$STAGING_DATA/config.json" + mkdir -p "$STAGING_DATA" + + # Docker may have created config.json as a directory + [ -d "$staging_config" ] && rmdir "$staging_config" 2>/dev/null || true + if [ ! -f "$prod_config" ]; then - warn "No config.json found at ${prod_config} — staging may not start correctly." + warn "No production config at ${prod_config} — staging may use defaults." return fi if [ ! -f "$staging_config" ] || [ "$prod_config" -nt "$staging_config" ]; then @@ -439,12 +453,71 @@ container_health() { # ─── Start / Stop / Restart ────────────────────────────────────────────── +# Ensure config.json exists in the data directory before starting +ensure_config() { + local data_dir="$1" + local config="$data_dir/config.json" + mkdir -p "$data_dir" + + # Docker may have created config.json as a directory from a prior failed mount + [ -d "$config" ] && rmdir "$config" 2>/dev/null || true + + if [ -f "$config" ]; then + return 0 + fi + + # Try to copy from repo root (legacy location) + if [ -f ./config.json ]; then + info "No config in data directory — copying from ./config.json" + cp ./config.json "$config" + return 0 + fi + + # Prompt admin + echo "" + warn "No config.json found in ${data_dir}/" + echo "" + echo " CoreScope needs a config.json to connect to MQTT brokers." + echo "" + echo " Options:" + echo " 1) Create from example (you'll edit MQTT settings after)" + echo " 2) I'll put one there myself (abort for now)" + echo "" + read -p " Choose [1/2]: " -n 1 -r + echo "" + + case $REPLY in + 1) + cp config.example.json "$config" + # Generate a random API key + if command -v openssl &>/dev/null; then + API_KEY=$(openssl rand -hex 16) + else + API_KEY=$(head -c 32 /dev/urandom | xxd -p | head -c 32) + fi + sed -i "s/your-secret-api-key-here/${API_KEY}/" "$config" 2>/dev/null || true + log "Created ${config} from example with random API key." + warn "Edit MQTT settings before connecting observers:" + echo " nano ${config}" + echo "" + ;; + *) + echo " Place your config.json at: ${config}" + echo " Then run this command again." + exit 0 + ;; + esac +} + cmd_start() { local WITH_STAGING=false if [ "$1" = "--with-staging" ]; then WITH_STAGING=true fi + # Always check prod config + ensure_config "$PROD_DATA" + if $WITH_STAGING; then # Prepare staging data and config prepare_staging_db @@ -736,10 +809,13 @@ cmd_backup() { warn "Database not found (container not running?)" fi - # Config - if [ -f config.json ]; then - cp config.json "$BACKUP_DIR/config.json" + # Config (now lives in data dir) + if [ -f "$PROD_DATA/config.json" ]; then + cp "$PROD_DATA/config.json" "$BACKUP_DIR/config.json" log "config.json" + elif [ -f config.json ]; then + cp config.json "$BACKUP_DIR/config.json" + log "config.json (legacy repo root)" fi # Caddyfile @@ -833,8 +909,8 @@ cmd_restore() { # Restore config if present if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then - cp "$CONFIG_FILE" ./config.json - log "config.json restored" + cp "$CONFIG_FILE" "$PROD_DATA/config.json" + log "config.json restored to ${PROD_DATA}/" fi # Restore Caddyfile if present