fix: unified 2.0.5 branch for readiness and installer regressions#222
fix: unified 2.0.5 branch for readiness and installer regressions#222
Conversation
…on-bump-and-ci-bun-retry"" This reverts commit 3279251.
There was a problem hiding this comment.
Pull request overview
This PR unifies two feature branches for v2.0.5: a version bump / CI Bun retry fix and a light-theme contrast fix for profile tags. It also merges governance and installer fixes from PR #221.
Changes:
- Version bump to 2.0.5 across
VERSION,apps/web/package.json, and the Bun base image in the Dockerfile - Installer fixes: remove unsupported
--progress plainflags from alldocker composecalls, addexit 1oncompose upfailure, update Watchtower default host port from 8088 → 18088 - Light-theme tag contrast fix: refactor
FormView.tsxto use the sharedgetTagColorClasshelper with category-aware, dark-mode-safe CSS classes
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
VERSION |
Bumped to 2.0.5 |
apps/web/package.json |
Version bumped to 2.0.5 (kept in sync with VERSION) |
docker/Dockerfile.unified |
Bun base image pinned to 1.3.10-alpine |
.github/workflows/tests.yml |
3-attempt Bun setup retry with version 1.3.10 pinned across all 4 CI jobs |
docker-compose.watchtower.yml |
Default Watchtower host port changed from 8088 to 18088 to avoid conflicts |
scripts/install.sh |
Removes --progress plain flag; adds exit 1 on compose up failure |
tests/test_install.bats |
New tests verifying --progress removal and installer fail-fast; updates port assertion |
apps/web/src/views/FormView.tsx |
Tags now use getTagColorClass() for category-aware, contrast-safe styling in both themes |
apps/web/src/lib/tags.test.ts |
Adds explicit dark-mode override assertions to tag color test suite |
apps/web/src/components/SettingsView.tsx |
Adds watchtower_endpoint/watchtower_error to interface; displays error inline |
apps/server/api/routes/system.py |
Introduces _probe_watchtower_api helper probing 4 known Watchtower endpoints; refactors get_update_method and trigger_update to use it |
apps/server/api/routes/profiles.py |
Adds data URI decoding, settings IP fallback, and explicit httpx.TimeoutException/HTTPError handlers |
apps/server/test_main.py |
New tests for TestUpdateMethodEndpoint and five new TestImageProxyEndpoint tests |
.github/copilot-instructions.md |
Adds session governance section establishing tasks.md precedence and release authorization rules |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@copilot open a new pull request to apply changes based on the comments in this thread |
…review feedback Co-authored-by: hessius <1499030+hessius@users.noreply.github.com>
fix: address review comments — copilot-instructions version, system.py docstring, E2E badge locators
Tailwind v4 compat mode generates @media(prefers-color-scheme:dark) blocks that override the app's .dark class toggle when the OS is in dark mode. Replace all Tailwind dark: utility classes on tags with custom CSS classes (tag-body, tag-flavor, etc.) in index.css using explicit .dark .tag-* selectors. This ensures proper contrast in both light and dark themes. - Add tag-body, tag-flavor, tag-process, tag-origin, tag-roast, tag-style CSS classes with light/dark variants - Update tags.ts to use new CSS classes instead of Tailwind utilities - Update tags.test.ts for new class names - Remove unused safelist entry from tailwind.config.js
…ror handling Add settings hydration in lifespan to populate os.environ from settings.json when env vars are empty. Expand parse_gemini_error to detect auth errors and return user-friendly messages. Strip display-only keys before persisting settings. Increase timeouts from 300s to 600s. Add 4 new tests.
| # Check for long-running generation / model stall patterns | ||
| if ( | ||
| 'timed out after' in error_text_lower | ||
| or 'deadline exceeded' in error_text_lower | ||
| or 'took too long' in error_text_lower | ||
| ): | ||
| return ( | ||
| "Profile generation timed out. Please retry; if this repeats, " | ||
| "reduce prompt complexity or use a stronger Gemini model." |
There was a problem hiding this comment.
The authentication error check at step 3 in parse_gemini_error includes 'timeout' in the network/connection errors check (line 357): if 'network' in error_text_lower or 'connection' in error_text_lower or 'timeout' in error_text_lower. This check runs before the new timeout check at lines 377–385.
If a Gemini CLI error message contains both 'timeout' and 'timed out after' (e.g. "Gemini CLI timed out after 600s"), the 'timeout' substring will match the network/connection check first, returning "Network error connecting to Gemini API" instead of the more specific "Profile generation timed out" message. The timeout-specific check at lines 377–385 will never be reached for such errors.
Consider moving the timeout-specific check (lines 376–385) before the general network/connection check, or removing 'timeout' from the network/connection condition so it falls through to the more specific check.
There was a problem hiding this comment.
Fixed in 8433d96 — moved the timeout-specific check (timed out after, deadline exceeded, took too long) before the general network/connection check so the more specific message is returned.
| s6_env_dir = "/var/run/s6/container_environment" | ||
| if not os.path.isdir(s6_env_dir): | ||
| return | ||
| try: | ||
| with open(os.path.join(s6_env_dir, var_name), "w") as f: | ||
| f.write(value) | ||
| except Exception: | ||
| pass # best-effort; logged at caller site if needed | ||
|
|
||
|
|
There was a problem hiding this comment.
The _write_s6_env function introduced in main.py (lines 82–95) is a duplicate of the already-existing _update_s6_env function in apps/server/api/routes/system.py (line 1140). Both functions write a value to /var/run/s6/container_environment/. The version in system.py is more complete (it logs a debug message when the dir is absent, and logs success/failure with a request_id). This duplication increases maintenance burden. Consider extracting both into a shared utility module (e.g., utils/s6_env.py) or importing _update_s6_env from system.py in main.py.
| s6_env_dir = "/var/run/s6/container_environment" | |
| if not os.path.isdir(s6_env_dir): | |
| return | |
| try: | |
| with open(os.path.join(s6_env_dir, var_name), "w") as f: | |
| f.write(value) | |
| except Exception: | |
| pass # best-effort; logged at caller site if needed | |
| # Delegate to the shared implementation in api.routes.system to avoid | |
| # duplicating s6 environment update logic in multiple places. | |
| try: | |
| from api.routes.system import _update_s6_env | |
| except Exception: | |
| # If the route module cannot be imported (e.g. during early startup | |
| # or in restricted test environments), fall back to a no-op to avoid | |
| # breaking application startup. | |
| return | |
| _update_s6_env(var_name, value) |
There was a problem hiding this comment.
Fixed in 8433d96 — extracted the shared logic to utils/s6_env.py with the more complete implementation (logging, debug messages). Both main.py and system.py now delegate to utils.s6_env.update_s6_env().
| "generateFailed404Route": "Failed to generate profile: The server route was not found (404). This is often a reverse-proxy/API path mismatch — verify your proxy forwards /api/* to MeticAI.", | ||
| "generateFailedTimeout": "Failed to generate profile: The AI request timed out. Please retry; if this repeats, reduce prompt complexity or try a stronger Gemini model.", | ||
| "generateFailedValidation": "Failed to generate profile: The AI generated an invalid profile schema. Please retry — a second attempt often succeeds.", |
There was a problem hiding this comment.
The new translation keys generateFailed404Route, generateFailedTimeout, and generateFailedValidation are only added to the en and sv locale files. The de, es, fr, and it locales are missing these keys. While i18next will typically fall back to the en locale, users with those languages configured will see English strings rather than their native language for these new error messages. The missing keys should be added to all locale files.
There was a problem hiding this comment.
Fixed in 8433d96 — added generateFailed404Route, generateFailedTimeout, and generateFailedValidation keys to all four missing locales (de, es, fr, it) with proper translations.
- Extract s6 env helper to shared utils/s6_env.py, update main.py and system.py to delegate to it - Move timeout-specific check before general network/connection check in parse_gemini_error to avoid incorrect match ordering - Add missing i18n translation keys (generateFailed404Route, generateFailedTimeout, generateFailedValidation) to de, es, fr, it locales
Summary
Unified branch for 2.0.5 readiness fixes, including installer regressions, light theme contrast, and profile generation.
Changes in this PR
Profile generation fix (latest):
lifespan()to populateos.environfromsettings.jsonwhen env vars are empty on container restart (with masked-value guard)parse_gemini_error()to detect auth/API key errors and return user-friendly messages instead of raw Gemini CLI outputgeminiApiKeyConfigured,geminiApiKeyMasked) before persisting settings to prevent corrupted values insettings.jsonLight/dark theme tag contrast fix:
dark:utility classes on tags with custom CSS classes (tag-body,tag-flavor, etc.) to bypass Tailwind v4 compat mode@media(prefers-color-scheme:dark)override.dark .tag-*selectors inindex.csstags.tsandtags.test.tsfor new class namesEarlier fixes (already in branch):
/api/*route aliases for coffee generation endpoints (404 regression)Validation
apps/server: 575 Python tests passed (test_main.py)apps/server: 19 logging tests passed (test_logging.py)apps/web: 251 vitest tests passedroot: 161 BATS tests passed