diff --git a/.gitignore b/.gitignore index a9faa8d..947d73e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .worktrees/ +.claude/ .venv/ .pytest_cache/ __pycache__/ diff --git a/deploy/deploy-test-server.sh b/deploy/deploy-test-server.sh index 4bf4516..5a268dc 100755 --- a/deploy/deploy-test-server.sh +++ b/deploy/deploy-test-server.sh @@ -23,6 +23,7 @@ trap cleanup EXIT INT HUP TERM COPYFILE_DISABLE=1 tar -czf "$archive" \ --exclude .git \ + --exclude .claude \ --exclude .venv \ --exclude __pycache__ \ --exclude .pytest_cache \ @@ -98,7 +99,15 @@ $sudo_cmd mkdir -p \ /var/lib/left4me/workshop_cache \ /var/lib/left4me/tmp -$sudo_cmd chown -R left4me:left4me /var/lib/left4me /opt/left4me +$sudo_cmd chown left4me:left4me \ + /var/lib/left4me \ + /var/lib/left4me/installation \ + /var/lib/left4me/overlays \ + /var/lib/left4me/instances \ + /var/lib/left4me/runtime \ + /var/lib/left4me/workshop_cache \ + /var/lib/left4me/tmp +$sudo_cmd chown -R left4me:left4me /opt/left4me mkdir -p "$repo_tmp" tar -xzf "$archive" -C "$repo_tmp" @@ -143,10 +152,6 @@ fi run_as_left4me /opt/left4me/.venv/bin/python -m pip install --upgrade pip run_as_left4me /opt/left4me/.venv/bin/pip install -e /opt/left4me/l4d2host -e /opt/left4me/l4d2web -run_left4me_with_env env \ - JOB_WORKER_ENABLED=false \ - /opt/left4me/.venv/bin/python -c "from l4d2web.app import create_app; create_app()" - run_as_left4me sh -c "cd /opt/left4me/l4d2web && set -a; . /etc/left4me/host.env; . /etc/left4me/web.env; set +a; env \ JOB_WORKER_ENABLED=false \ PYTHONPATH=/opt/left4me \ diff --git a/deploy/tests/test_deploy_artifacts.py b/deploy/tests/test_deploy_artifacts.py index 132c395..83bcaeb 100644 --- a/deploy/tests/test_deploy_artifacts.py +++ b/deploy/tests/test_deploy_artifacts.py @@ -33,10 +33,11 @@ def test_web_unit_contains_required_runtime_contract(): assert "EnvironmentFile=/etc/left4me/web.env" in unit assert "ExecStart=/opt/left4me/.venv/bin/gunicorn" in unit assert "--workers 1" in unit - assert "NoNewPrivileges=true" in unit - assert "PrivateTmp=true" in unit + assert "NoNewPrivileges=true" not in unit + assert "PrivateTmp=true" not in unit assert "ProtectSystem=full" in unit assert "ReadWritePaths=/var/lib/left4me" in unit + assert "MountFlags=shared" in unit def test_server_unit_contains_required_runtime_contract(): @@ -169,6 +170,7 @@ def test_deploy_script_has_safe_defaults_and_preserves_state() -> None: assert "/var/lib/left4me/runtime" in script assert "tar" in script assert "--exclude .venv" in script + assert "--exclude .claude" in script assert "pip install -e /opt/left4me/l4d2host -e /opt/left4me/l4d2web" in script assert "systemctl enable --now left4me-web.service" in script assert "for attempt in" in script @@ -183,5 +185,21 @@ def test_deploy_script_has_safe_defaults_and_preserves_state() -> None: assert "deploy/files" in script +def test_deploy_script_does_not_recurse_into_runtime_state_mounts() -> None: + script = DEPLOY_SCRIPT.read_text() + + assert "$sudo_cmd chown -R left4me:left4me /var/lib/left4me" not in script + assert "$sudo_cmd chown left4me:left4me \\" in script + assert "/var/lib/left4me/runtime \\" in script + assert "$sudo_cmd chown -R left4me:left4me /opt/left4me" in script + + +def test_deploy_script_runs_migrations_before_app_initialization() -> None: + script = DEPLOY_SCRIPT.read_text() + + assert "alembic -c /opt/left4me/l4d2web/alembic.ini upgrade head" in script + assert "from l4d2web.app import create_app; create_app()" not in script + + def test_deploy_script_shell_syntax() -> None: subprocess.run(["sh", "-n", str(DEPLOY_SCRIPT)], check=True)