mirror of
https://github.com/wu736139669/OpsPilot.git
synced 2026-08-05 06:23:35 +00:00
feat: add auto-update detection via version check API
Adds GET /api/version endpoint that compares local git commit against origin/HEAD. Dashboard shows a clickable update banner when new commits are available. Also adds POST /api/git/pull for one-click updates. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -157,6 +157,9 @@
|
||||
<div class="header-left">
|
||||
<div class="logo">⚡ <span>OpsPilot</span></div>
|
||||
<span><span class="dot live" id="live-dot"></span> <span id="conn-text" style="font-size:11px;color:var(--text-muted);">Connecting...</span></span>
|
||||
<span id="update-banner" style="display:none;font-size:10px;background:#fef3c7;color:#92400e;padding:3px 10px;border-radius:4px;cursor:pointer;" onclick="runGitPull()" title="Click to git pull">
|
||||
⬆ Update available · <span id="update-count"></span> commits behind
|
||||
</span>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<span>🖥 <strong id="stat-agents">0</strong> Agents</span>
|
||||
@@ -905,8 +908,33 @@ initSplitter('splitter-1', '.col-left', 160);
|
||||
// splitter-2: drag to resize col-right (min 300px)
|
||||
initSplitter('splitter-2', '.col-right', 300);
|
||||
|
||||
// ── Version check ──
|
||||
async function checkForUpdates() {
|
||||
try {
|
||||
const r = await fetch('/api/version');
|
||||
const v = await r.json();
|
||||
if (v.update_available) {
|
||||
document.getElementById('update-banner').style.display = 'inline';
|
||||
document.getElementById('update-count').textContent = v.commits_behind;
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
async function runGitPull() {
|
||||
toast('Pulling latest code...');
|
||||
try {
|
||||
const r = await api('POST', '/api/git/pull');
|
||||
toast(r.message || 'Updated! Please restart the server.');
|
||||
document.getElementById('update-banner').style.display = 'none';
|
||||
} catch(e) {
|
||||
toast('Pull failed. Run manually: cd OpsPilot && git pull');
|
||||
}
|
||||
}
|
||||
|
||||
// Init
|
||||
connect();
|
||||
checkForUpdates();
|
||||
setInterval(checkForUpdates, 300000); // Check every 5 min
|
||||
setInterval(() => { if (ws && ws.readyState === WebSocket.OPEN) ws.send('ping'); }, 8000);
|
||||
setInterval(() => {
|
||||
document.getElementById('stat-time').textContent = new Date().toLocaleTimeString('zh-CN');
|
||||
|
||||
@@ -140,6 +140,57 @@ async def get_state():
|
||||
return await _build_state()
|
||||
|
||||
|
||||
@app.post("/api/git/pull")
|
||||
async def git_pull():
|
||||
"""Pull latest code from GitHub."""
|
||||
import subprocess as _sp
|
||||
try:
|
||||
r = _sp.run(["git", "pull", "origin", "main"], capture_output=True, text=True, cwd=str(BASE_DIR), timeout=30)
|
||||
add_log("info", "update", f"git pull: {r.stdout.strip() or r.stderr.strip()}")
|
||||
if "Already up to date" in r.stdout or "Already up-to-date" in r.stdout:
|
||||
return {"status": "ok", "message": "Already up to date"}
|
||||
return {"status": "ok", "message": r.stdout.strip() or "Updated"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/version")
|
||||
async def check_version():
|
||||
"""Check current version and compare with latest GitHub release."""
|
||||
import subprocess as _sp
|
||||
info = {
|
||||
"current_commit": "",
|
||||
"current_short": "",
|
||||
"latest_commit": "",
|
||||
"latest_short": "",
|
||||
"update_available": False,
|
||||
"commits_behind": 0,
|
||||
}
|
||||
try:
|
||||
# Get current commit
|
||||
r = _sp.run(["git", "rev-parse", "HEAD"], capture_output=True, text=True, cwd=str(BASE_DIR), timeout=5)
|
||||
if r.returncode == 0:
|
||||
info["current_commit"] = r.stdout.strip()
|
||||
info["current_short"] = info["current_commit"][:7]
|
||||
# Get latest from remote (without pulling)
|
||||
r2 = _sp.run(["git", "ls-remote", "origin", "HEAD"], capture_output=True, text=True, timeout=10)
|
||||
if r2.returncode == 0 and r2.stdout.strip():
|
||||
info["latest_commit"] = r2.stdout.split()[0]
|
||||
info["latest_short"] = info["latest_commit"][:7]
|
||||
if info["current_commit"] and info["latest_commit"] and info["current_commit"] != info["latest_commit"]:
|
||||
# Count commits behind
|
||||
r3 = _sp.run(
|
||||
["git", "rev-list", "--count", f"{info['current_commit']}..{info['latest_commit']}"],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
if r3.returncode == 0:
|
||||
info["commits_behind"] = int(r3.stdout.strip())
|
||||
info["update_available"] = info["commits_behind"] > 0
|
||||
except Exception:
|
||||
pass
|
||||
return info
|
||||
|
||||
|
||||
@app.post("/api/agents")
|
||||
async def register_agent(data: dict):
|
||||
"""Register or update an agent."""
|
||||
|
||||
Reference in New Issue
Block a user