[gemma-4-multimodal] Visual Verification of Deployment Success #1490
146
scripts/deploy_verify.py
Normal file
146
scripts/deploy_verify.py
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Deployment Visual Verification
|
||||
==============================
|
||||
|
||||
Post-deployment step that uses vision to verify UI is rendered correctly.
|
||||
Takes screenshots of deployed endpoints and checks for:
|
||||
- Page rendering errors
|
||||
- Missing assets
|
||||
- Layout breaks
|
||||
- Error messages visible
|
||||
- Expected content present
|
||||
|
||||
Usage:
|
||||
python scripts/deploy_verify.py check https://my-app.com
|
||||
python scripts/deploy_verify.py check https://my-app.com --expect "Welcome"
|
||||
python scripts/deploy_verify.py batch urls.txt
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class DeployCheck:
|
||||
"""A single deployment verification check."""
|
||||
url: str
|
||||
status: str # passed, failed, warning
|
||||
issues: list = field(default_factory=list)
|
||||
screenshot_path: Optional[str] = None
|
||||
expected_content: str = ""
|
||||
timestamp: str = ""
|
||||
|
||||
def summary(self) -> str:
|
||||
emoji = {"passed": "✅", "failed": "❌", "warning": "⚠️"}.get(self.status, "❓")
|
||||
lines = [
|
||||
f"{emoji} {self.url}",
|
||||
f" Checked: {self.timestamp or 'pending'}",
|
||||
]
|
||||
if self.expected_content:
|
||||
lines.append(f" Expected: '{self.expected_content}'")
|
||||
if self.issues:
|
||||
lines.append(" Issues:")
|
||||
for i in self.issues:
|
||||
lines.append(f" - {i}")
|
||||
else:
|
||||
lines.append(" No issues detected")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
class DeployVerifier:
|
||||
"""Verifies deployed UI renders correctly using screenshots."""
|
||||
|
||||
def build_check_prompt(self, url: str, expected: str = "") -> dict:
|
||||
"""Build verification prompt for a deployed URL."""
|
||||
expect_clause = ""
|
||||
if expected:
|
||||
expect_clause = f"\n- Verify the text \"{expected}\" is visible on the page"
|
||||
|
||||
prompt = f"""Take a screenshot of {url} and verify the deployment is healthy.
|
||||
|
||||
Check for:
|
||||
- Page loads without errors (no 404, 500, connection refused)
|
||||
- No visible error messages or stack traces
|
||||
- Layout is not broken (elements properly aligned, no overlapping)
|
||||
- Images and assets load correctly (no broken image icons)
|
||||
- Navigation elements are present and clickable{expect_clause}
|
||||
- No "under construction" or placeholder content
|
||||
- Responsive design elements render properly
|
||||
|
||||
Return as JSON:
|
||||
```json
|
||||
{{
|
||||
"status": "passed|failed|warning",
|
||||
"issues": ["list of issues found"],
|
||||
"confidence": 0.9,
|
||||
"page_title": "detected page title",
|
||||
"visible_text_sample": "first 100 chars of visible text"
|
||||
}}
|
||||
```
|
||||
"""
|
||||
return {
|
||||
"url": url,
|
||||
"prompt": prompt,
|
||||
"screenshot_needed": True,
|
||||
"instruction": f"browser_navigate to {url}, take screenshot with browser_vision, analyze with prompt"
|
||||
}
|
||||
|
||||
def verify_deployment(self, url: str, expected: str = "", screenshot_path: str = "") -> DeployCheck:
|
||||
"""Create a deployment verification check."""
|
||||
check = DeployCheck(
|
||||
url=url,
|
||||
status="pending",
|
||||
expected_content=expected,
|
||||
timestamp=datetime.now().isoformat(),
|
||||
screenshot_path=screenshot_path or f"/tmp/deploy_verify_{url.replace('://', '_').replace('/', '_')}.png"
|
||||
)
|
||||
return check
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: deploy_verify.py <check|batch> [args...]")
|
||||
return 1
|
||||
|
||||
verifier = DeployVerifier()
|
||||
cmd = sys.argv[1]
|
||||
|
||||
if cmd == "check":
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: deploy_verify.py check <url> [--expect 'text']")
|
||||
return 1
|
||||
url = sys.argv[2]
|
||||
expected = ""
|
||||
if "--expect" in sys.argv:
|
||||
idx = sys.argv.index("--expect")
|
||||
if idx + 1 < len(sys.argv):
|
||||
expected = sys.argv[idx + 1]
|
||||
|
||||
result = verifier.build_check_prompt(url, expected)
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
elif cmd == "batch":
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: deploy_verify.py batch <urls_file>")
|
||||
return 1
|
||||
urls_file = Path(sys.argv[2])
|
||||
if not urls_file.exists():
|
||||
print(f"File not found: {urls_file}")
|
||||
return 1
|
||||
|
||||
urls = [line.strip() for line in urls_file.read_text().splitlines() if line.strip() and not line.startswith("#")]
|
||||
for url in urls:
|
||||
print(f"\n--- {url} ---")
|
||||
result = verifier.build_check_prompt(url)
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user