Migrate declarative configs to config/ dir and remove hardcoded paths

Replace all hardcoded /home/wall/vacuum-wall paths in lib/ with Path(__file__).resolve()
auto-discovery. Move config files from data/ to config/<subsystem>/config.json.
ACME now uses ACME_HOME env var and data/acme/ for cert storage. Systemd units
and sudoers use {{ USER_NAME }}, {{ PROJECT_DIR }}, {{ ACME_HOME }} Jinja2
template variables for install-time substitution. Remove sys.path.insert boot
strap from test files.
This commit is contained in:
2026-05-14 03:31:13 +00:00
parent 817b7c409c
commit d9797b6dac
16 changed files with 131 additions and 136 deletions
+20 -13
View File
@@ -2,7 +2,7 @@
ACME certificate manager for Vacuum Wall.
Wraps acme.sh to issue, renew, and manage SSL/TLS certificates
from Let's Encrypt (or other ACME providers). acme.sh runs as the
from ACME providers such as ZeroSSL or Let's Encrypt. acme.sh runs as the
vacuum-wall system user; nginx is reloaded via a deploy hook script.
"""
@@ -16,16 +16,17 @@ from pathlib import Path
logger = logging.getLogger(__name__)
PROJECT_DIR = Path(__file__).resolve().parent.parent
_ACME_HOME = PROJECT_DIR / "data" / "acme"
_DEPLOY_HOOK = str(PROJECT_DIR / "system" / "acme-deploy.sh")
_ACME_ENVIRON = {
"HOME": str(Path.home()),
"HOME": str(PROJECT_DIR),
"PATH": os.environ.get(
"PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
),
}
PROJECT_DIR = Path("/home/wall/vacuum-wall")
_DEPLOY_HOOK = str(PROJECT_DIR / "system" / "acme-deploy.sh")
def _find_acme() -> str:
"""Locate the acme.sh binary on the system.
@@ -41,7 +42,7 @@ def _find_acme() -> str:
FileNotFoundError: If acme.sh cannot be found.
"""
candidates = [
Path.home() / ".acme.sh" / "acme.sh",
_ACME_HOME / "acme.sh",
Path("/usr/local/bin/acme.sh"),
]
@@ -81,12 +82,15 @@ def _run_acme(args: list[str]) -> str:
"""
acme_bin = _find_acme()
# Check for ACME_HOME env var (set by systemd in production)
acme_home_env = os.environ.get("ACME_HOME", str(_ACME_HOME))
cmd: list[str] = [
acme_bin,
"--home",
str(Path.home() / ".acme.sh"),
acme_home_env,
"--config-home",
str(Path.home() / ".acme.sh"),
acme_home_env,
*args,
]
@@ -131,7 +135,8 @@ def set_email(email: str) -> None:
def get_email() -> str:
"""Return the ACME contact email, or '' if none is configured."""
try:
account_conf = Path.home() / ".acme.sh" / "account.conf"
acme_home = Path(os.environ.get("ACME_HOME", str(_ACME_HOME)))
account_conf = acme_home / "account.conf"
if account_conf.is_file():
text = account_conf.read_text()
match = re.search(r"^ACME_LEEMAIL=(.+)$", text, re.MULTILINE)
@@ -246,7 +251,8 @@ def list_certs() -> list[dict]:
certs: list[dict] = []
entries = _parse_list_output(raw)
acme_home = Path.home() / ".acme.sh"
acme_home_env = os.environ.get("ACME_HOME", str(_ACME_HOME))
acme_home = Path(acme_home_env)
for entry in entries:
main = entry["main_domain"]
@@ -393,7 +399,8 @@ def get_cert_paths(domain: str) -> dict:
Returns:
Dict with keys 'cert', 'key', 'ca', 'fullchain' mapped to paths.
"""
acme_home = str(Path.home() / ".acme.sh" / domain)
acme_home_env = os.environ.get("ACME_HOME", str(_ACME_HOME))
acme_home = str(Path(acme_home_env) / domain)
return {
"cert": f"{acme_home}/{domain}.cert",
"key": f"{acme_home}/{domain}.key",
@@ -484,6 +491,6 @@ def _has_auto_renew(domain: str) -> bool:
under ``~/.acme.sh/``; existence of this file means the systemd
timer's ``--cron`` run will pick it up.
"""
acme_home = Path.home() / ".acme.sh"
domain_conf = acme_home / f"{domain}.conf"
acme_home_env = os.environ.get("ACME_HOME", str(_ACME_HOME))
domain_conf = Path(acme_home_env) / f"{domain}.conf"
return bool(domain_conf.is_file())