Track version
This commit is contained in:
parent
1bec9320b4
commit
114af496c7
5 changed files with 114 additions and 0 deletions
2
.github/workflows/docker-publish.yml
vendored
2
.github/workflows/docker-publish.yml
vendored
|
|
@ -36,6 +36,8 @@ jobs:
|
|||
push: true
|
||||
no-cache: true
|
||||
pull: true
|
||||
build-args: |
|
||||
COMMIT_SHA=${{ github.sha }}
|
||||
tags: |
|
||||
boulderbadgedad/soulsync:latest
|
||||
boulderbadgedad/soulsync:${{ inputs.version_tag }}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@
|
|||
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Build-time commit SHA for update detection
|
||||
ARG COMMIT_SHA=""
|
||||
ENV SOULSYNC_COMMIT_SHA=${COMMIT_SHA}
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
|
|
|
|||
|
|
@ -11925,6 +11925,64 @@ def _automatic_wishlist_cleanup_after_db_update():
|
|||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# ── Update detection ─────────────────────────────────────────────
|
||||
_GITHUB_REPO = "Nezreka/SoulSync"
|
||||
_update_cache = {'latest_sha': None, 'last_check': 0, 'error': None}
|
||||
_UPDATE_CHECK_INTERVAL = 3600 # 1 hour
|
||||
|
||||
def _get_current_commit_sha():
|
||||
"""Get the commit SHA of the running instance (env var for Docker, git for local)."""
|
||||
# Docker: baked in at build time via COMMIT_SHA build arg
|
||||
sha = os.environ.get('SOULSYNC_COMMIT_SHA', '').strip()
|
||||
if sha:
|
||||
return sha
|
||||
# Local dev: read from git
|
||||
try:
|
||||
import subprocess
|
||||
result = subprocess.run(['git', 'rev-parse', 'HEAD'], capture_output=True, text=True, cwd=os.path.dirname(__file__) or '.')
|
||||
if result.returncode == 0:
|
||||
return result.stdout.strip()
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
_current_commit_sha = _get_current_commit_sha()
|
||||
|
||||
def _check_for_updates():
|
||||
"""Check GitHub for the latest commit SHA on main branch."""
|
||||
import time as _time
|
||||
now = _time.time()
|
||||
if now - _update_cache['last_check'] < _UPDATE_CHECK_INTERVAL:
|
||||
return # Still fresh
|
||||
_update_cache['last_check'] = now
|
||||
try:
|
||||
import urllib.request
|
||||
import json as _json
|
||||
req = urllib.request.Request(
|
||||
f"https://api.github.com/repos/{_GITHUB_REPO}/commits/main",
|
||||
headers={'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'SoulSync-UpdateCheck'}
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
data = _json.loads(resp.read().decode())
|
||||
_update_cache['latest_sha'] = data.get('sha')
|
||||
_update_cache['error'] = None
|
||||
except Exception as e:
|
||||
_update_cache['error'] = str(e)
|
||||
logger.debug(f"Update check failed: {e}")
|
||||
|
||||
@app.route('/api/update-check', methods=['GET'])
|
||||
def check_for_update():
|
||||
"""Check if a newer version is available on GitHub."""
|
||||
_check_for_updates()
|
||||
current = _current_commit_sha
|
||||
latest = _update_cache.get('latest_sha')
|
||||
update_available = bool(current and latest and current != latest)
|
||||
return jsonify({
|
||||
'update_available': update_available,
|
||||
'current_sha': current[:8] if current else None,
|
||||
'latest_sha': latest[:8] if latest else None,
|
||||
})
|
||||
|
||||
@app.route('/api/version-info', methods=['GET'])
|
||||
def get_version_info():
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1241,6 +1241,10 @@ function initApp() {
|
|||
fetchAndUpdateServiceStatus();
|
||||
setInterval(fetchAndUpdateServiceStatus, 10000); // Every 10 seconds (no-op when WebSocket active)
|
||||
|
||||
// Check for updates on load and every hour
|
||||
checkForUpdates();
|
||||
setInterval(checkForUpdates, 3600000);
|
||||
|
||||
// Refresh key data immediately when user returns to this tab
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) {
|
||||
|
|
@ -12076,7 +12080,36 @@ function formatArtists(artists) {
|
|||
return artistNames.join(', ') || 'Unknown Artist';
|
||||
}
|
||||
|
||||
async function checkForUpdates() {
|
||||
try {
|
||||
const res = await fetch('/api/update-check');
|
||||
if (!res.ok) return;
|
||||
const data = await res.json();
|
||||
const btn = document.querySelector('.version-button');
|
||||
if (!btn) return;
|
||||
if (data.update_available) {
|
||||
const dismissed = localStorage.getItem('soulsync-update-dismissed');
|
||||
if (dismissed !== data.latest_sha) {
|
||||
btn.classList.add('update-available');
|
||||
}
|
||||
} else {
|
||||
btn.classList.remove('update-available');
|
||||
}
|
||||
} catch (e) {
|
||||
console.debug('Update check failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function showVersionInfo() {
|
||||
// Dismiss update glow when user opens the modal (fire-and-forget, don't block modal)
|
||||
const btn = document.querySelector('.version-button');
|
||||
if (btn && btn.classList.contains('update-available')) {
|
||||
btn.classList.remove('update-available');
|
||||
fetch('/api/update-check').then(r => r.json()).then(data => {
|
||||
if (data.latest_sha) localStorage.setItem('soulsync-update-dismissed', data.latest_sha);
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('Fetching version info...');
|
||||
|
||||
|
|
|
|||
|
|
@ -868,6 +868,23 @@ body {
|
|||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.version-button.update-available {
|
||||
color: rgb(var(--accent-light-rgb));
|
||||
border-color: rgba(var(--accent-rgb), 0.4);
|
||||
animation: version-glow 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes version-glow {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 4px rgba(var(--accent-rgb), 0.2), 0 0 8px rgba(var(--accent-rgb), 0.1);
|
||||
border-color: rgba(var(--accent-rgb), 0.3);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 8px rgba(var(--accent-rgb), 0.5), 0 0 20px rgba(var(--accent-rgb), 0.3), 0 0 30px rgba(var(--accent-rgb), 0.1);
|
||||
border-color: rgba(var(--accent-rgb), 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
/* Status Section - Premium Glassmorphic Design */
|
||||
.status-section {
|
||||
height: fit-content;
|
||||
|
|
|
|||
Loading…
Reference in a new issue