From 84a132399dea6988af1d73f68075765417bd77bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Manso?= Date: Thu, 20 Aug 2026 11:08:18 +0100 Subject: [PATCH] fix(build): make dev-prod-db honor env KANDEV_DATABASE_PATH / KANDEV_HOME_DIR (#2834) Co-authored-by: Joao Manso Co-authored-by: Carlos Florencio --- Makefile | 28 +++++++- scripts/dev-prod-db-path.test.sh | 115 +++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 scripts/dev-prod-db-path.test.sh diff --git a/Makefile b/Makefile index 3f84bf4e0e..cc8ad301c8 100644 --- a/Makefile +++ b/Makefile @@ -96,7 +96,7 @@ help: @echo " dev Run backend + web via the native Go launcher (auto ports)" @echo " dev PORT=38430 WEB_PORT=37430 PORT beats KANDEV_BACKEND_PORT/KANDEV_PORT, WEB_PORT beats KANDEV_WEB_PORT" @echo " dev DEV_ARGS='--verbose' Pass extra flags through to the native launcher" - @echo " dev-prod-db Run dev mode against the production db at ~/.kandev" + @echo " dev-prod-db Run dev mode against the production db (KANDEV_DATABASE_PATH, else KANDEV_HOME_DIR, else ~/.kandev)" @echo " dev-backend Run backend in development mode (port 38429)" @echo " dev-web Run web app in development mode (port 37429)" @echo " desktop-dev Run macOS Tauri app in dev mode with bundled runtime" @@ -201,9 +201,30 @@ dev: doctor @exec $(BACKEND_DIR)/bin/kandev-launcher$(EXE) dev $(DEV_FLAGS) .PHONY: dev-prod-db -dev-prod-db: export KANDEV_DATABASE_PATH := $(HOME)/.kandev/data/kandev.db +# Resolve the production db the way the launcher would +# (resolveDatabasePath/resolveHomeDir in apps/backend/internal/launcher/constants.go): +# an environment KANDEV_DATABASE_PATH wins, then KANDEV_HOME_DIR, then +# $HOME/.kandev. A plain makefile assignment outranks the environment in GNU +# Make, so the earlier `:=` of the $HOME default ignored a relocated install and +# ran dev mode against the wrong db while the launcher still reported backing up +# the production one. +# +# `?=` does not express this either: Make counts a variable the shell exported +# blank as defined, so `?=` would forward that blank onward and the launcher — +# which trims before testing for empty — would quietly fall back to the isolated +# .kandev-dev db while this target announced production. +# +# $(if $(strip …),…) tests emptiness on the trimmed value but substitutes the +# original, so a blank falls through to the next source while a real path keeps +# the internal spaces $(strip …) would otherwise collapse. Export the Make +# variable so both environment and command-line assignments reach $(shell). +# Trim only the outer padding before the database suffix is appended. +export KANDEV_HOME_DIR +KANDEV_PROD_HOME_DIR := $(if $(strip $(KANDEV_HOME_DIR)),$(shell printf '%s' "$$KANDEV_HOME_DIR" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$$//'),$(HOME)/.kandev) +KANDEV_PROD_DB_PATH := $(if $(strip $(KANDEV_DATABASE_PATH)),$(KANDEV_DATABASE_PATH),$(KANDEV_PROD_HOME_DIR)/data/kandev.db) +dev-prod-db: export KANDEV_DATABASE_PATH := $(KANDEV_PROD_DB_PATH) dev-prod-db: - @echo "⚠ dev mode against PRODUCTION db at $(KANDEV_DATABASE_PATH)" + @echo "⚠ dev mode against PRODUCTION db at $(KANDEV_PROD_DB_PATH)" @$(MAKE) dev .PHONY: dev-backend @@ -540,6 +561,7 @@ test-scripts: @python3 .github/scripts/lint-action-pinning_test.py @bash scripts/pr-state.test.sh @bash scripts/run-quiet.test.sh + @bash scripts/dev-prod-db-path.test.sh @bash scripts/opencode-code-review.test.sh @python3 scripts/opencode-code-review.test.py @python3 scripts/lint-harness-files.test.py diff --git a/scripts/dev-prod-db-path.test.sh b/scripts/dev-prod-db-path.test.sh new file mode 100644 index 0000000000..0f2c83bbaa --- /dev/null +++ b/scripts/dev-prod-db-path.test.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash +# dev-prod-db-path.test.sh — assert the root Makefile's `dev-prod-db` target +# exports the same production database path the launcher would resolve on its +# own: an environment-provided KANDEV_DATABASE_PATH wins, then KANDEV_HOME_DIR, +# then $HOME/.kandev (resolveDatabasePath/resolveHomeDir in +# apps/backend/internal/launcher/constants.go). +# +# The pre-fix target assigned with `:=`, which outranks the environment in GNU +# Make. On an install whose KANDEV_HOME_DIR points outside $HOME, that silently +# pointed dev mode at the untouched $HOME/.kandev default while the launcher +# still printed "backed up production db", so the warning named a database the +# run never opened. +# +# Each case is a dry run. `-n` alone is not enough: make executes any recipe +# line mentioning $(MAKE) even under -n, so `@$(MAKE) dev` would recurse into a +# real build. Overriding MAKE swaps that line for a probe that prints the +# exported value and swallows the leftover `dev` argument on a `:` builtin. That +# neutralises the recursion and asserts the variable as the child process +# actually receives it rather than as the sibling echo renders it. `:` is used +# rather than a trailing `#` so the value carries nothing a make implementation +# might re-read as a comment. +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PROBE_MAKE='printenv KANDEV_DATABASE_PATH || echo UNSET; :' +status=0 + +# resolve ... — the KANDEV_DATABASE_PATH dev-prod-db exports. +resolve() { + env "$@" make -C "$ROOT_DIR" --no-print-directory MAKE="$PROBE_MAKE" \ + -n dev-prod-db 2>/dev/null | tail -n 1 +} + +resolve_make_home() { + env -u KANDEV_DATABASE_PATH make -C "$ROOT_DIR" --no-print-directory MAKE="$PROBE_MAKE" \ + -n dev-prod-db "KANDEV_HOME_DIR=$1" 2>/dev/null | tail -n 1 +} + +expect_eq() { + local label=$1 want=$2 got=$3 + if [ "$got" = "$want" ]; then + printf 'ok %-29s %s\n' "$label" "$got" + else + printf 'FAIL %-29s\n want: %s\n got: %s\n' "$label" "$want" "$got" + status=1 + fi +} + +expect_suffix() { + local label=$1 want=$2 got=$3 + # $HOME is asserted by suffix: Git Bash/MSYS rewrites HOME on its way to + # native make, so the absolute prefix is not reproducible across shells. + case "$got" in + UNSET | "") ;; + *"$want") + printf 'ok %-29s %s\n' "$label" "$got" + return + ;; + esac + printf 'FAIL %-29s\n want suffix: %s\n got: %s\n' \ + "$label" "$want" "$got" + status=1 +} + +# 1. Clean environment — the historical default under $HOME. +expect_suffix "default home" "/.kandev/data/kandev.db" \ + "$(resolve -u KANDEV_DATABASE_PATH -u KANDEV_HOME_DIR)" + +# 2. Relocated home, no explicit db path — derived from KANDEV_HOME_DIR. The +# backslash value mirrors the Windows install that surfaced the bug; make +# joins with a forward slash, which Go accepts alongside backslashes. +expect_eq "KANDEV_HOME_DIR" 'D:\kandev-probe/data/kandev.db' \ + "$(resolve -u KANDEV_DATABASE_PATH 'KANDEV_HOME_DIR=D:\kandev-probe')" + +# 3. Explicit db path — forwarded unchanged, and it outranks KANDEV_HOME_DIR. +expect_eq "KANDEV_DATABASE_PATH" 'D:\kandev-probe\data\custom.db' \ + "$(resolve 'KANDEV_DATABASE_PATH=D:\kandev-probe\data\custom.db' 'KANDEV_HOME_DIR=D:\other')" + +# 4. A whitespace-only KANDEV_HOME_DIR is not a relocated install. resolveHomeDir +# trims before testing for empty, so the Makefile must fall back to $HOME +# rather than derive " /data/kandev.db" from it. +expect_suffix "blank KANDEV_HOME_DIR" "/.kandev/data/kandev.db" \ + "$(resolve -u KANDEV_DATABASE_PATH 'KANDEV_HOME_DIR= ')" + +# 5. An exported-but-empty KANDEV_DATABASE_PATH must not be forwarded. Make +# counts it as defined, so `?=` would keep the blank; the launcher trims it, +# finds nothing, and drops to the isolated .kandev-dev db — dev-prod-db would +# then announce production while opening the dev database. +expect_eq "empty KANDEV_DATABASE_PATH" 'D:\kandev-probe/data/kandev.db' \ + "$(resolve 'KANDEV_DATABASE_PATH=' 'KANDEV_HOME_DIR=D:\kandev-probe')" + +# 6. Same for a whitespace-only override, which the launcher also trims away. +expect_eq "blank KANDEV_DATABASE_PATH" 'D:\kandev-probe/data/kandev.db' \ + "$(resolve 'KANDEV_DATABASE_PATH= ' 'KANDEV_HOME_DIR=D:\kandev-probe')" + +# 7. Internal whitespace is part of the path. $(strip …) collapses runs of +# spaces, so it may gate the emptiness test but must not supply the value. +expect_eq "spaces inside KANDEV_HOME_DIR" 'D:\kandev probe/data/kandev.db' \ + "$(resolve -u KANDEV_DATABASE_PATH 'KANDEV_HOME_DIR=D:\kandev probe')" + +# 8. Leading and trailing padding around a nonblank home must be removed +# before the database suffix is appended. Otherwise the trailing spaces +# become internal path characters and survive the launcher's TrimSpace. +expect_eq "padded KANDEV_HOME_DIR" 'D:\kandev-probe/data/kandev.db' \ + "$(resolve -u KANDEV_DATABASE_PATH 'KANDEV_HOME_DIR= D:\kandev-probe ')" + +# 9. A Make command-line assignment must use the same trimmed home path. The +# explicit environment database is cleared so the home remains the source. +expect_eq "Make variable home" 'D:\kandev-probe/data/kandev.db' \ + "$(resolve_make_home 'D:\kandev-probe ')" + +if [ "$status" -eq 0 ]; then + echo "All dev-prod-db path checks passed (env db path > KANDEV_HOME_DIR > \$HOME/.kandev)." +fi +exit "$status"