mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 03:01:21 +02:00
- Docker update detection for in-app updates
- Added _is_docker_environment() function - Check endpoint returns is_docker and update_method fields - Apply endpoint rejects Docker with helpful instructions - Added updates API tests - Updated CHANGELOG 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
7e3fcec02d
commit
1234e44eb2
@@ -5,6 +5,7 @@ All notable changes to Bambuddy will be documented in this file.
|
||||
## [0.1.6b4] - 2026-01-01
|
||||
|
||||
### Added
|
||||
- **Docker update detection** - Automatically detects Docker installations and shows appropriate update instructions instead of failing with git errors
|
||||
- **Camera popup window improvements**
|
||||
- Auto-resize to fit video resolution on first open
|
||||
- Persist window size and position to localStorage
|
||||
|
||||
@@ -27,6 +27,20 @@ _update_status = {
|
||||
}
|
||||
|
||||
|
||||
def _is_docker_environment() -> bool:
|
||||
"""Detect if running inside a Docker container."""
|
||||
if os.path.exists("/.dockerenv"):
|
||||
return True
|
||||
try:
|
||||
with open("/proc/1/cgroup") as f:
|
||||
if "docker" in f.read():
|
||||
return True
|
||||
except (FileNotFoundError, PermissionError):
|
||||
pass
|
||||
git_dir = settings.base_dir / ".git"
|
||||
return not git_dir.exists()
|
||||
|
||||
|
||||
def _find_executable(name: str) -> str | None:
|
||||
"""Find an executable in PATH or common locations."""
|
||||
# Try standard PATH first
|
||||
@@ -199,6 +213,7 @@ async def check_for_updates(db: AsyncSession = Depends(get_db)):
|
||||
"error": None,
|
||||
}
|
||||
|
||||
is_docker = _is_docker_environment()
|
||||
return {
|
||||
"update_available": update_available,
|
||||
"current_version": APP_VERSION,
|
||||
@@ -207,6 +222,8 @@ async def check_for_updates(db: AsyncSession = Depends(get_db)):
|
||||
"release_notes": release_notes,
|
||||
"release_url": release_url,
|
||||
"published_at": published_at,
|
||||
"is_docker": is_docker,
|
||||
"update_method": "docker" if is_docker else "git",
|
||||
}
|
||||
|
||||
except httpx.HTTPError as e:
|
||||
@@ -426,6 +443,18 @@ async def apply_update(background_tasks: BackgroundTasks):
|
||||
"status": _update_status,
|
||||
}
|
||||
|
||||
# Check if running in Docker
|
||||
if _is_docker_environment():
|
||||
return {
|
||||
"success": False,
|
||||
"is_docker": True,
|
||||
"message": (
|
||||
"Docker installations cannot be updated in-app. "
|
||||
"Please update via Docker Compose: "
|
||||
"git pull && docker compose build --pull && docker compose up -d"
|
||||
),
|
||||
}
|
||||
|
||||
# Start update in background
|
||||
background_tasks.add_task(_perform_update)
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
"""Integration tests for Updates API endpoints."""
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from httpx import AsyncClient
|
||||
|
||||
|
||||
class TestUpdatesAPI:
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_version(self, async_client: AsyncClient):
|
||||
response = await async_client.get("/api/v1/updates/version")
|
||||
assert response.status_code == 200
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_apply_update_docker_rejection(self, async_client: AsyncClient):
|
||||
with patch("backend.app.api.routes.updates._is_docker_environment", return_value=True):
|
||||
response = await async_client.post("/api/v1/updates/apply")
|
||||
result = response.json()
|
||||
assert result["success"] is False
|
||||
assert result["is_docker"] is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_apply_update_non_docker(self, async_client: AsyncClient):
|
||||
"""Test non-Docker path - mock _perform_update to prevent side effects."""
|
||||
with (
|
||||
patch("backend.app.api.routes.updates._is_docker_environment", return_value=False),
|
||||
patch("backend.app.api.routes.updates._perform_update", new_callable=AsyncMock),
|
||||
):
|
||||
response = await async_client.post("/api/v1/updates/apply")
|
||||
assert response.json()["success"] is True
|
||||
|
||||
def test_is_docker_with_dockerenv(self):
|
||||
from backend.app.api.routes.updates import _is_docker_environment
|
||||
|
||||
with patch("os.path.exists", return_value=True):
|
||||
assert _is_docker_environment() is True
|
||||
|
||||
def test_parse_version(self):
|
||||
from backend.app.api.routes.updates import parse_version
|
||||
|
||||
assert parse_version("0.1.5")[:3] == (0, 1, 5)
|
||||
|
||||
def test_is_newer_version(self):
|
||||
from backend.app.api.routes.updates import is_newer_version
|
||||
|
||||
assert is_newer_version("0.1.5", "0.1.5b7") is True
|
||||
Reference in New Issue
Block a user