mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 12:03:59 +00:00
Fix installer path portability (avoid hardcoded /Users paths)
- Rewrite stale foreign home prefixes when loading env defaults (macOS <-> Linux)
- Write /Users/chrisfu-relative values to env.sh; sanitize prole.cfg paths to use ${HOME}
- Remove remaining /Users/chrisfu defaults from helper scripts and OpenTofu kubeconfig path
- Make installer tests more reliable by forcing repo root to front of sys.path
Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
parent
2dc8726395
commit
caa2685fc2
@ -6,9 +6,9 @@
|
||||
; User-editable values; derived values below reference these by default.
|
||||
NAMESPACE = knoe-db
|
||||
PROLE_CONF = ${PROLE_HOME}/conf
|
||||
PROLE_DATA = /Users/chrisfu/.prole/data
|
||||
PROLE_HOME = /Users/chrisfu/dev/prole
|
||||
PROLE_LOGS = /opt/prole/logs/chrisfu
|
||||
PROLE_DATA = ${HOME}/.prole/data
|
||||
PROLE_HOME = ${HOME}/dev/prole
|
||||
PROLE_LOGS = /opt/prole/logs/${USER}
|
||||
PROLE_SERVICE = ${PROLE_HOME}/etc
|
||||
SERVICE_NAMESPACE = knoe-system
|
||||
|
||||
@ -166,7 +166,7 @@ MODE = k3s
|
||||
PIPELINE_URL = http://127.0.0.1:8080
|
||||
|
||||
[Prod Cluster (k8s)]
|
||||
ARTIFACTS_DIR = /Users/chrisfu/dev/prole/data/staging
|
||||
ARTIFACTS_DIR = ${PROLE_HOME}/data/staging
|
||||
CLUSTER_ENV = prole-prod-cluster
|
||||
DISPLAY_NAME = prole-prod-cluster
|
||||
MODE = k8s
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
k3s_server_url = "https://myrddin.prole.org:6443"
|
||||
k3s_token = "K107c8c6000488eca4a067d8a73119bbae2f07b4ea1bac7d8d3dc9c500cbb8acb18::server:04572345810eae2f9619a6ed4239702b"
|
||||
namespace = "prole-db"
|
||||
kubeconfig_path = "/Users/chrisfu/dev/prole/prole-k3s.kubeconfig"
|
||||
kubeconfig_path = "../../../prole-k3s.kubeconfig"
|
||||
|
||||
11
env.sh
11
env.sh
@ -3,11 +3,12 @@
|
||||
# This file is generated by the installer. Source it in new shells, or execute as a wrapper:
|
||||
# "$PROLE_HOME/env.sh" <command> [args…]
|
||||
# shellcheck shell=bash
|
||||
export PROLE_HOME="/Users/chrisfu/dev/prole"
|
||||
export PROLE_CONF="/Users/chrisfu/dev/prole/conf"
|
||||
export PROLE_DATA="/Users/chrisfu/.prole/data"
|
||||
export PROLE_LOGS="/opt/prole/logs/chrisfu"
|
||||
export PROLE_SERVICE="/Users/chrisfu/dev/prole/etc"
|
||||
PROLE_HOME="${PROLE_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}"
|
||||
export PROLE_HOME
|
||||
export PROLE_CONF="${PROLE_CONF:-$PROLE_HOME/conf}"
|
||||
export PROLE_DATA="${PROLE_DATA:-$HOME/.prole/data}"
|
||||
export PROLE_LOGS="${PROLE_LOGS:-/opt/prole/logs/${USER}}"
|
||||
export PROLE_SERVICE="${PROLE_SERVICE:-$PROLE_HOME/etc}"
|
||||
|
||||
# Ensure PATH works for GUI-launched shells (Docker, etc.)
|
||||
_prole_add_path() { case ":${PATH}:" in *":$1:"*) ;; *) PATH="$1:${PATH:-}" ;; esac; }
|
||||
|
||||
@ -11,7 +11,7 @@ SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
# shellcheck disable=SC1090
|
||||
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||
|
||||
PROLE_HOME="${PROLE_HOME:-/Users/chrisfu/dev/prole}"
|
||||
PROLE_HOME="${PROLE_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
||||
VERBOSE=0
|
||||
PROLE_CFG_OVERRIDE=""
|
||||
PORT_MAPPING_FILE_PATH=""
|
||||
|
||||
@ -462,6 +462,17 @@ class ProleInstallerBase:
|
||||
# --------------------------------------------- cfg sanitisation
|
||||
def _sanitize_sections_for_cfg(self, sections: dict) -> dict:
|
||||
sanitized = {k: dict(v) for k, v in sections.items()}
|
||||
|
||||
# Avoid persisting machine-specific home paths (e.g. /Users/<user>/...) in prole.cfg.
|
||||
# Use ${HOME} where possible, and also rewrite stale foreign-home paths that match
|
||||
# the current username (common when HOME is shared across machines).
|
||||
for section_name, section in sanitized.items():
|
||||
for k, v in list(section.items()):
|
||||
if not isinstance(v, str) or not v.strip():
|
||||
continue
|
||||
if _is_prole_secret(v) or _is_openbao_ref(v):
|
||||
continue
|
||||
section[k] = self._cfgify_home_path(v)
|
||||
if "Kerberos Authentication" in sanitized:
|
||||
val = sanitized["Kerberos Authentication"].get("PASSWORD", "")
|
||||
if val or ("Kerberos Authentication", "PASSWORD") in self._cfg_secret_cache:
|
||||
@ -590,6 +601,92 @@ class ProleInstallerBase:
|
||||
return "".join(secrets.choice(alphabet) for _ in range(length))
|
||||
|
||||
# --------------------------------------------------- env helpers
|
||||
|
||||
_FOREIGN_HOME_PREFIX_RE = re.compile(r"^/(Users|home)/([^/]+)(/.*)?$")
|
||||
|
||||
def _rewrite_foreign_home_prefix(self, value: str) -> str:
|
||||
"""Rewrite foreign home prefixes to the current user's home.
|
||||
|
||||
Example: running on Linux with HOME=/home/<user>, rewrite
|
||||
`/Users/<user>/dev/prole` -> `/home/<user>/dev/prole`.
|
||||
|
||||
This helps prevent stale macOS paths (often stored in `~/.prole/env.sh`) from
|
||||
being persisted into `prole.cfg` on Linux.
|
||||
"""
|
||||
|
||||
raw = (value or "").strip()
|
||||
if not raw or "$" in raw:
|
||||
return raw
|
||||
|
||||
m = self._FOREIGN_HOME_PREFIX_RE.match(raw)
|
||||
if not m:
|
||||
return raw
|
||||
|
||||
foreign_root = m.group(1)
|
||||
foreign_user = m.group(2)
|
||||
suffix = m.group(3) or ""
|
||||
|
||||
try:
|
||||
current_home = Path.home()
|
||||
except Exception:
|
||||
return raw
|
||||
|
||||
# Only rewrite when the path refers to the current username.
|
||||
if foreign_user != current_home.name:
|
||||
return raw
|
||||
|
||||
system = platform.system()
|
||||
if system != "Darwin" and foreign_root == "Users":
|
||||
return str(current_home) + suffix
|
||||
if system == "Darwin" and foreign_root == "home":
|
||||
return str(current_home) + suffix
|
||||
|
||||
return raw
|
||||
|
||||
def _shellify_home_path(self, value: str) -> str:
|
||||
"""Represent paths under the current home directory using `$HOME`.
|
||||
|
||||
Keeps values unchanged if they already contain shell variables.
|
||||
"""
|
||||
|
||||
raw = (value or "").strip()
|
||||
if not raw or "$" in raw:
|
||||
return raw
|
||||
try:
|
||||
p = Path(raw).expanduser()
|
||||
home = Path.home()
|
||||
rel = p.relative_to(home)
|
||||
except Exception:
|
||||
return raw
|
||||
rel_str = rel.as_posix()
|
||||
if not rel_str or rel_str == ".":
|
||||
return "$HOME"
|
||||
return f"$HOME/{rel_str}"
|
||||
|
||||
def _cfgify_home_path(self, value: str) -> str:
|
||||
"""Represent paths under the current home directory using `${HOME}`.
|
||||
|
||||
Also rewrites stale foreign-home paths like `/Users/<user>/...` when the
|
||||
username matches the current user.
|
||||
"""
|
||||
|
||||
raw = (value or "").strip()
|
||||
if not raw or "$" in raw:
|
||||
return raw
|
||||
|
||||
raw = self._rewrite_foreign_home_prefix(raw)
|
||||
|
||||
try:
|
||||
p = Path(raw).expanduser()
|
||||
home = Path.home()
|
||||
rel = p.relative_to(home)
|
||||
except Exception:
|
||||
return raw
|
||||
rel_str = rel.as_posix()
|
||||
if not rel_str or rel_str == ".":
|
||||
return "${HOME}"
|
||||
return f"${{HOME}}/{rel_str}"
|
||||
|
||||
def _env_defaults(self, namespace: str | None = None) -> dict:
|
||||
default_home = Path.home() / ".prole"
|
||||
resolved_home = self._resolve_env_value("PROLE_HOME", str(default_home)) or str(
|
||||
@ -662,7 +759,17 @@ class ProleInstallerBase:
|
||||
line = line[len("export ") :]
|
||||
if "=" in line:
|
||||
k, v = line.split("=", 1)
|
||||
env[k.strip()] = v.strip().strip('"')
|
||||
k = k.strip()
|
||||
v = v.strip().strip('"')
|
||||
if k in {
|
||||
"PROLE_HOME",
|
||||
"PROLE_CONF",
|
||||
"PROLE_DATA",
|
||||
"PROLE_LOGS",
|
||||
"PROLE_SERVICE",
|
||||
}:
|
||||
v = self._rewrite_foreign_home_prefix(v)
|
||||
env[k] = v
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
@ -696,7 +803,7 @@ class ProleInstallerBase:
|
||||
"PROLE_LOGS",
|
||||
"PROLE_SERVICE",
|
||||
):
|
||||
content.append(f'export {k}="{values[k]}"')
|
||||
content.append(f'export {k}="{self._shellify_home_path(values[k])}"')
|
||||
# NAMESPACE/PROLE_NAMESPACE must never be written to env.sh — only prole.cfg is the source of truth
|
||||
content.append("")
|
||||
content.append("# Ensure PATH works for GUI-launched shells (Docker, etc.)")
|
||||
|
||||
@ -11,7 +11,7 @@ SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
# shellcheck disable=SC1090
|
||||
source "$SCRIPT_DIR/prole_cfg.sh"
|
||||
|
||||
PROLE_HOME="${PROLE_HOME:-/Users/chrisfu/dev/prole}"
|
||||
PROLE_HOME="${PROLE_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
||||
VERBOSE=0
|
||||
PROLE_CFG_OVERRIDE=""
|
||||
PORT_MAPPING_FILE_PATH=""
|
||||
|
||||
@ -4,11 +4,13 @@ if [[ -z "${MSSQL_SA_PASSWORD:-}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROLE_HOME="${PROLE_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
||||
|
||||
DOCKER_DEFAULT_PLATFORM=linux/amd64 docker run --rm -it \
|
||||
--hostname=prole-mssql-db001 -p 1433:1433 \
|
||||
-e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$MSSQL_SA_PASSWORD" \
|
||||
--env=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
|
||||
--volume=/Users/chrisfu/dev/prole/mssql/data:/var/opt/mssql/data \
|
||||
--volume="${PROLE_HOME}/mssql/data:/var/opt/mssql/data" \
|
||||
--entrypoint bash \
|
||||
--restart=no \
|
||||
mcr.microsoft.com/mssql/server:2022-latest
|
||||
|
||||
@ -4,11 +4,13 @@ if [[ -z "${MSSQL_SA_PASSWORD:-}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROLE_HOME="${PROLE_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
||||
|
||||
DOCKER_DEFAULT_PLATFORM=linux/amd64 docker run --rm -it \
|
||||
--hostname=prole-mssql-db001 -p 1433:1433 \
|
||||
-e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$MSSQL_SA_PASSWORD" \
|
||||
--env=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
|
||||
--volume=/Users/chrisfu/dev/prole/mssql/data:/var/opt/mssql/data \
|
||||
--volume="${PROLE_HOME}/mssql/data:/var/opt/mssql/data" \
|
||||
--entrypoint bash \
|
||||
--restart=no \
|
||||
prole-mssql-db:016
|
||||
|
||||
@ -6,7 +6,9 @@ if [[ -z "${MSSQL_SA_PASSWORD:-}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROLE_HOME="${PROLE_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
||||
|
||||
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=$MSSQL_SA_PASSWORD" \
|
||||
-p 1433:1433 --name mssql-server --platform linux/amd64 \
|
||||
-v /Users/chrisfu/dev/prole/mssql/data:/var/opt/mssql/data \
|
||||
-v "${PROLE_HOME}/mssql/data:/var/opt/mssql/data" \
|
||||
-d mcr.microsoft.com/mssql/server:2022-latest
|
||||
|
||||
@ -4,11 +4,13 @@ if [[ -z "${MSSQL_SA_PASSWORD:-}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROLE_HOME="${PROLE_HOME:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
||||
|
||||
DOCKER_DEFAULT_PLATFORM=linux/amd64 docker run --rm -it \
|
||||
--hostname=prole-mssql-db-1 \
|
||||
-p 1433:1433 \
|
||||
-e "ACCEPT_EULA=Y" \
|
||||
-e "MSSQL_SA_PASSWORD=$MSSQL_SA_PASSWORD" \
|
||||
-e "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
|
||||
-v /Users/chrisfu/dev/prole/mssql/data:/var/opt/mssql/data \
|
||||
-v "${PROLE_HOME}/mssql/data:/var/opt/mssql/data" \
|
||||
prole-mssql-db:015
|
||||
|
||||
@ -6,9 +6,16 @@ test_screen.py don't hit exhausted side-effect iterators left over from
|
||||
test_navigation.py (which replaces sys.modules["tkinter"] at module level).
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
# Ensure the repository root is first on sys.path so we import the local
|
||||
# `installer` package (and not a site-packages name collision).
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(_REPO_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_REPO_ROOT))
|
||||
|
||||
import installer.screen as _screen_module
|
||||
|
||||
|
||||
|
||||
@ -332,6 +332,68 @@ class TestNamespaceHelpers:
|
||||
assert inst._registry_namespace() == "knoe-system"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# env.sh portability (avoid hardcoded /Users/<user> paths)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestEnvPortability:
|
||||
def test_env_defaults_rewrites_foreign_macos_home_paths_on_linux(
|
||||
self, tmp_path, monkeypatch
|
||||
):
|
||||
# Simulate a shared/synced HOME that contains an env.sh written on macOS.
|
||||
fake_home = tmp_path / "testuser"
|
||||
(fake_home / ".prole").mkdir(parents=True)
|
||||
(fake_home / ".prole" / "env.sh").write_text(
|
||||
'\n'.join(
|
||||
[
|
||||
'export PROLE_HOME="/Users/testuser/dev/prole"',
|
||||
'export PROLE_CONF="/Users/testuser/dev/prole/conf"',
|
||||
'export PROLE_DATA="/Users/testuser/.prole/data"',
|
||||
"",
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
inst = _TestableInstaller(project_root=tmp_path)
|
||||
|
||||
monkeypatch.setattr("installer.core.actions.platform.system", lambda: "Linux")
|
||||
monkeypatch.setattr("installer.core.actions.Path.home", lambda: fake_home)
|
||||
|
||||
with mock.patch.dict(os.environ, {}, clear=False):
|
||||
os.environ.pop("PROLE_HOME", None)
|
||||
os.environ.pop("PROLE_CONF", None)
|
||||
os.environ.pop("PROLE_DATA", None)
|
||||
env = inst._env_defaults(namespace="prole-db")
|
||||
|
||||
assert env["PROLE_HOME"] == str(fake_home / "dev" / "prole")
|
||||
assert env["PROLE_CONF"] == str(fake_home / "dev" / "prole" / "conf")
|
||||
assert env["PROLE_DATA"] == str(fake_home / ".prole" / "data")
|
||||
|
||||
def test_save_env_to_file_uses_home_var_not_absolute_home(self, tmp_path, monkeypatch):
|
||||
fake_home = tmp_path / "testuser"
|
||||
prole_home = fake_home / "dev" / "prole"
|
||||
(prole_home / "conf").mkdir(parents=True)
|
||||
(prole_home / "etc").mkdir(parents=True)
|
||||
|
||||
inst = _TestableInstaller(project_root=tmp_path)
|
||||
monkeypatch.setattr("installer.core.actions.Path.home", lambda: fake_home)
|
||||
|
||||
values = {
|
||||
"PROLE_HOME": str(prole_home),
|
||||
"PROLE_CONF": str(prole_home / "conf"),
|
||||
"PROLE_DATA": str(fake_home / ".prole" / "data"),
|
||||
"PROLE_LOGS": "/opt/prole/logs/chrisfu",
|
||||
"PROLE_SERVICE": str(prole_home / "etc"),
|
||||
}
|
||||
|
||||
inst._save_env_to_file(values)
|
||||
|
||||
env_sh = (prole_home / "env.sh").read_text()
|
||||
assert 'export PROLE_HOME="$HOME/dev/prole"' in env_sh
|
||||
assert str(fake_home) not in env_sh
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _read_k3s_cfg_values
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@ -47,9 +47,10 @@ fi
|
||||
|
||||
# Test 4: prole-agent can run from scan directory
|
||||
echo "4. Testing prole-agent runs from writable directory..."
|
||||
PROLE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
mkdir -p /tmp/scan-test
|
||||
cd /tmp/scan-test
|
||||
/Users/chrisfu/dev/prole/prole-net/prole-agent 2>&1 &
|
||||
"$PROLE_ROOT/prole-net/prole-agent" 2>&1 &
|
||||
SCAN_PID=$!
|
||||
sleep 2
|
||||
if ps -p $SCAN_PID > /dev/null 2>&1; then
|
||||
@ -60,7 +61,7 @@ else
|
||||
echo " ✗ prole-agent failed to start"
|
||||
exit 1
|
||||
fi
|
||||
cd /Users/chrisfu/dev/prole
|
||||
cd "$PROLE_ROOT"
|
||||
|
||||
# Test 5: Code uses scan directory
|
||||
echo "5. Testing code uses scan directory..."
|
||||
|
||||
Loading…
Reference in New Issue
Block a user