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
|
push: true
|
||||||
no-cache: true
|
no-cache: true
|
||||||
pull: true
|
pull: true
|
||||||
|
build-args: |
|
||||||
|
COMMIT_SHA=${{ github.sha }}
|
||||||
tags: |
|
tags: |
|
||||||
boulderbadgedad/soulsync:latest
|
boulderbadgedad/soulsync:latest
|
||||||
boulderbadgedad/soulsync:${{ inputs.version_tag }}
|
boulderbadgedad/soulsync:${{ inputs.version_tag }}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@
|
||||||
|
|
||||||
FROM python:3.11-slim
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
# Build-time commit SHA for update detection
|
||||||
|
ARG COMMIT_SHA=""
|
||||||
|
ENV SOULSYNC_COMMIT_SHA=${COMMIT_SHA}
|
||||||
|
|
||||||
# Set working directory
|
# Set working directory
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11925,6 +11925,64 @@ def _automatic_wishlist_cleanup_after_db_update():
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
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'])
|
@app.route('/api/version-info', methods=['GET'])
|
||||||
def get_version_info():
|
def get_version_info():
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1241,6 +1241,10 @@ function initApp() {
|
||||||
fetchAndUpdateServiceStatus();
|
fetchAndUpdateServiceStatus();
|
||||||
setInterval(fetchAndUpdateServiceStatus, 10000); // Every 10 seconds (no-op when WebSocket active)
|
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
|
// Refresh key data immediately when user returns to this tab
|
||||||
document.addEventListener('visibilitychange', () => {
|
document.addEventListener('visibilitychange', () => {
|
||||||
if (!document.hidden) {
|
if (!document.hidden) {
|
||||||
|
|
@ -12076,7 +12080,36 @@ function formatArtists(artists) {
|
||||||
return artistNames.join(', ') || 'Unknown Artist';
|
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() {
|
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 {
|
try {
|
||||||
console.log('Fetching version info...');
|
console.log('Fetching version info...');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -868,6 +868,23 @@ body {
|
||||||
transform: scale(0.97);
|
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 - Premium Glassmorphic Design */
|
||||||
.status-section {
|
.status-section {
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue