mirror of
https://github.com/ArchiveBox/ArchiveBox.git
synced 2025-05-18 17:14:39 -04:00
Move version banner logic from JS to Python
Also adds CSS styling to banner.
This commit is contained in:
parent
7599dbb79d
commit
a3fd8a8ecd
3 changed files with 111 additions and 78 deletions
|
@ -30,6 +30,7 @@ import inspect
|
||||||
import getpass
|
import getpass
|
||||||
import platform
|
import platform
|
||||||
import shutil
|
import shutil
|
||||||
|
import requests
|
||||||
import django
|
import django
|
||||||
from sqlite3 import dbapi2 as sqlite3
|
from sqlite3 import dbapi2 as sqlite3
|
||||||
|
|
||||||
|
@ -397,6 +398,55 @@ def get_commit_hash(config):
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_version_releases(config):
|
||||||
|
"""
|
||||||
|
returns a dictionary containing the GitHub release data for
|
||||||
|
the recommended upgrade version and the currently installed version
|
||||||
|
"""
|
||||||
|
github_releases_api = "https://api.github.com/repos/pirate/archivebox/releases"
|
||||||
|
response = requests.get(github_releases_api)
|
||||||
|
if response.status_code != 200:
|
||||||
|
stderr('Failed to get release data from GitHub', color='lightyellow', config=config)
|
||||||
|
return None
|
||||||
|
|
||||||
|
releases = response.json()
|
||||||
|
installed_version = config['VERSION']
|
||||||
|
installed_version_parts = parse_tag_name(installed_version)
|
||||||
|
|
||||||
|
# find current version or nearest older version (to link to)
|
||||||
|
current_version = None
|
||||||
|
for i, release in enumerate(releases):
|
||||||
|
release_parts = parse_tag_name(release["tag_name"])
|
||||||
|
if compare_versions(release["tag_name"], installed_version) <= 0:
|
||||||
|
current_version = release
|
||||||
|
break
|
||||||
|
|
||||||
|
current_version = current_version if current_version else releases[-1]
|
||||||
|
|
||||||
|
# find upgrade version
|
||||||
|
upgrade_version = None
|
||||||
|
smallest_version_diff = parse_tag_name(releases[0]["tag_name"])[1]
|
||||||
|
for i, release in enumerate(releases):
|
||||||
|
release_parts = parse_tag_name(release["tag_name"])
|
||||||
|
major_version_diff = release_parts[1] - installed_version_parts[1]
|
||||||
|
if major_version_diff < smallest_version_diff:
|
||||||
|
smallest_version_diff = major_version_diff
|
||||||
|
if smallest_version_diff < 1:
|
||||||
|
break
|
||||||
|
upgrade_version = release
|
||||||
|
|
||||||
|
upgrade_version = upgrade_version if upgrade_version else releases[0]
|
||||||
|
|
||||||
|
return {"upgrade_version": upgrade_version, "current_version": current_version}
|
||||||
|
|
||||||
|
def can_upgrade(config):
|
||||||
|
if config['VERSION_RELEASES']:
|
||||||
|
upgrade_version_tag = config['VERSION_RELEASES']['upgrade_version']['tag_name']
|
||||||
|
current_version_tag = config['VERSION_RELEASES']['current_version']['tag_name']
|
||||||
|
return compare_versions(upgrade_version_tag, current_version_tag) == 1
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
############################## Derived Config ##################################
|
############################## Derived Config ##################################
|
||||||
|
|
||||||
|
|
||||||
|
@ -424,6 +474,8 @@ DYNAMIC_CONFIG_SCHEMA: ConfigDefaultDict = {
|
||||||
|
|
||||||
'ARCHIVEBOX_BINARY': {'default': lambda c: sys.argv[0] or bin_path('archivebox')},
|
'ARCHIVEBOX_BINARY': {'default': lambda c: sys.argv[0] or bin_path('archivebox')},
|
||||||
'VERSION': {'default': lambda c: get_version(c)},
|
'VERSION': {'default': lambda c: get_version(c)},
|
||||||
|
'VERSION_RELEASES': {'default': lambda c: get_version_releases(c)},
|
||||||
|
'CAN_UPGRADE': {'default': lambda c: can_upgrade(c)},
|
||||||
'COMMIT_HASH': {'default': lambda c: get_commit_hash(c)},
|
'COMMIT_HASH': {'default': lambda c: get_commit_hash(c)},
|
||||||
|
|
||||||
'PYTHON_BINARY': {'default': lambda c: sys.executable},
|
'PYTHON_BINARY': {'default': lambda c: sys.executable},
|
||||||
|
@ -696,6 +748,28 @@ def load_config(defaults: ConfigDefaultDict,
|
||||||
|
|
||||||
# with open(os.path.join(config['OUTPUT_DIR'], CONFIG_FILENAME), 'w+') as f:
|
# with open(os.path.join(config['OUTPUT_DIR'], CONFIG_FILENAME), 'w+') as f:
|
||||||
|
|
||||||
|
def parse_tag_name(v):
|
||||||
|
"""parses a version tag string formatted like 'vx.x.x'"""
|
||||||
|
v = re.sub(r"\+.*$", "", v) # in case version string ends with '+editable'
|
||||||
|
parts = re.sub(r"^v", "", v).split(".")
|
||||||
|
return [int(p) for p in parts]
|
||||||
|
|
||||||
|
|
||||||
|
def compare_versions(v1, v2):
|
||||||
|
"""
|
||||||
|
for two version strings v1 and v2, returns 1 if v1 is newer than v2,
|
||||||
|
0 if they're equivalent and -1 if v1 is older than v2.
|
||||||
|
"""
|
||||||
|
v1Parts = parse_tag_name(v1)
|
||||||
|
v2Parts = parse_tag_name(v2)
|
||||||
|
for i in range(len(v1Parts)):
|
||||||
|
if v1Parts[i] < v2Parts[i]:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if v1Parts[i] > v2Parts[i]:
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
# Logging Helpers
|
# Logging Helpers
|
||||||
def stdout(*args, color: Optional[str]=None, prefix: str='', config: Optional[ConfigDict]=None) -> None:
|
def stdout(*args, color: Optional[str]=None, prefix: str='', config: Optional[ConfigDict]=None) -> None:
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django.views.generic.base import RedirectView
|
||||||
|
|
||||||
from core.views import HomepageView, SnapshotView, PublicIndexView, AddView, HealthCheckView
|
from core.views import HomepageView, SnapshotView, PublicIndexView, AddView, HealthCheckView
|
||||||
|
|
||||||
from config import VERSION
|
from config import VERSION, VERSION_RELEASES, CAN_UPGRADE
|
||||||
|
|
||||||
# print('DEBUG', settings.DEBUG)
|
# print('DEBUG', settings.DEBUG)
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ urlpatterns = [
|
||||||
|
|
||||||
|
|
||||||
path('accounts/', include('django.contrib.auth.urls')),
|
path('accounts/', include('django.contrib.auth.urls')),
|
||||||
path('admin/', admin.site.urls, {'extra_context': {'VERSION': VERSION}}),
|
path('admin/', admin.site.urls, {'extra_context': {'VERSION': VERSION, 'VERSION_RELEASES': VERSION_RELEASES, 'CAN_UPGRADE': CAN_UPGRADE}}),
|
||||||
|
|
||||||
path('health/', HealthCheckView.as_view(), name='healthcheck'),
|
path('health/', HealthCheckView.as_view(), name='healthcheck'),
|
||||||
path('error/', lambda _: 1/0),
|
path('error/', lambda _: 1/0),
|
||||||
|
|
|
@ -12,7 +12,26 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}">
|
<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}">
|
||||||
{% block extrastyle %}{% endblock %}
|
{% block extrastyle %}
|
||||||
|
<style>
|
||||||
|
#upgrade-banner {
|
||||||
|
position: fixed;
|
||||||
|
right: 20px;
|
||||||
|
bottom: 20px;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
color: #333333;
|
||||||
|
border: 2px solid #772948;
|
||||||
|
padding: 10px 20px;
|
||||||
|
z-index: 1000;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#dismiss-btn {
|
||||||
|
background: #aa1e55;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% if LANGUAGE_BIDI %}
|
{% if LANGUAGE_BIDI %}
|
||||||
<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}">
|
<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}">
|
||||||
|
@ -123,99 +142,39 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const installedVersion = "{{VERSION}}";
|
if ("{{CAN_UPGRADE}}" === "True" && !localStorage.getItem("bannerDismissed")) {
|
||||||
|
let upgradeVersionTag = "{{VERSION_RELEASES.upgrade_version.tag_name}}"
|
||||||
|
let upgradeVersionURL= "{{VERSION_RELEASES.upgrade_version.html_url}}"
|
||||||
|
let currentVersionTag = "{{VERSION}}"
|
||||||
|
let currentVersionURL = "{{VERSION_RELEASES.current_version.html_url}}"
|
||||||
|
|
||||||
// get versions from GitHub
|
createBanner(currentVersionTag, currentVersionURL, upgradeVersionTag, upgradeVersionURL)
|
||||||
const github_releases_api = "https://api.github.com/repos/pirate/archivebox/releases";
|
|
||||||
|
|
||||||
let release_data = fetch(github_releases_api)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(release_data => {
|
|
||||||
let upgradeVersion = findUpgradeVersion(installedVersion, release_data);
|
|
||||||
let currentVersion = findCurrentVersion(installedVersion, release_data);
|
|
||||||
|
|
||||||
const showBanner = localStorage.getItem("bannerDismissed") !== "true" && currentVersion.html_url !== upgradeVersion.html_url
|
|
||||||
if (showBanner) {
|
|
||||||
createBanner(currentVersion, upgradeVersion);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error fetching release data: ', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
// finds the nearest stable version
|
|
||||||
function findCurrentVersion(currentVersionTagName, releaseData) {
|
|
||||||
for (let i = 0; i < releaseData.length; i++) {
|
|
||||||
if (compareVersions(releaseData[i].tag_name, currentVersionTagName) <= 0) {
|
|
||||||
return releaseData[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return releaseData[releaseData.length - 1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function findUpgradeVersion(currentVersionTagName, releaseData) {
|
function createBanner(currentVersionTag, currentVersionURL, upgradeVersionTag, upgradeVersionURL){
|
||||||
for (let i = 0; i < releaseData.length; i++) {
|
|
||||||
if (majorVersionDiff(releaseData[i].tag_name, currentVersionTagName) === 1) {
|
|
||||||
return releaseData[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return releaseData[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function createBanner(currentVersion, upgradeVersion) {
|
|
||||||
const banner = document.createElement('div');
|
const banner = document.createElement('div');
|
||||||
banner.setAttribute('id', 'upgrade-banner');
|
banner.setAttribute('id', 'upgrade-banner');
|
||||||
banner.innerHTML = `
|
banner.innerHTML = `
|
||||||
There's a new version of ArchiveBox available!
|
<p>There's a new version of ArchiveBox available!</p>
|
||||||
The next major version is <a href=${upgradeVersion.html_url}>${upgradeVersion.tag_name}</a>.
|
Your version: <a href=${currentVersionURL}>${currentVersionTag}</a> | New version: <a href=${upgradeVersionURL}>${upgradeVersionTag}</a>
|
||||||
Your current version is <a href=${currentVersion.html_url}>${installedVersion}</a>
|
|
||||||
<p>
|
<p>
|
||||||
<a href=https://github.com/ArchiveBox/ArchiveBox/wiki/Upgrading-or-Merging-Archives>Upgrading</a> | <a href=https://github.com/ArchiveBox/ArchiveBox/releases>Changelog</a> | <a href=https://github.com/ArchiveBox/ArchiveBox/wiki/Roadmap>Roadmap</a>
|
<a href=https://github.com/ArchiveBox/ArchiveBox/wiki/Upgrading-or-Merging-Archives>Upgrading</a> | <a href=https://github.com/ArchiveBox/ArchiveBox/releases>Changelog</a> | <a href=https://github.com/ArchiveBox/ArchiveBox/wiki/Roadmap>Roadmap</a>
|
||||||
</p>
|
</p>
|
||||||
<button>
|
<button id="dismiss-btn">Dismiss</button>
|
||||||
<a href="#" onclick="dismissBanner()">Dismiss</a>
|
|
||||||
</button>
|
|
||||||
`
|
`
|
||||||
document.body.appendChild(banner);
|
document.body.appendChild(banner);
|
||||||
|
let dismissButton = document.querySelector("#dismiss-btn")
|
||||||
|
if (dismissButton) {
|
||||||
|
dismissButton.addEventListener("click", dismissBanner)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// dismisses the version banner and stores a cookie to prevent it from showing again
|
|
||||||
function dismissBanner() {
|
function dismissBanner() {
|
||||||
var banner = document.getElementById("version-banner");
|
var banner = document.getElementById("upgrade-banner");
|
||||||
banner.style.display = "none";
|
banner.style.display = "none";
|
||||||
localStorage.setItem("bannerDismissed", "true");
|
localStorage.setItem("bannerDismissed", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseVersion(v) {
|
|
||||||
return v.replace(/^v/, '').split(".").map(Number);
|
|
||||||
}
|
|
||||||
|
|
||||||
// compares two version strings formatted like "vx.x.x" (where the x's are integers)
|
|
||||||
// and returns 1 if v1 is newer than v2, 0 if they're the same, and -1
|
|
||||||
// if v1 is older than v2.
|
|
||||||
function compareVersions(v1, v2) {
|
|
||||||
let v1Parts = parseVersion(v1);
|
|
||||||
let v2Parts = parseVersion(v2);
|
|
||||||
|
|
||||||
for (let i = 0; i < 3; i++) {
|
|
||||||
if (v1Parts[i] < v2Parts[i]) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (v1Parts[i] > v2Parts[i]) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function majorVersionDiff(v1, v2) {
|
|
||||||
let v1Parts = parseVersion(v1);
|
|
||||||
let v2Parts = parseVersion(v2);
|
|
||||||
|
|
||||||
return v1Parts[1] - v2Parts[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
$ = django.jQuery;
|
$ = django.jQuery;
|
||||||
$.fn.reverse = [].reverse;
|
$.fn.reverse = [].reverse;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue