From 2aa6aba3bced9c38bf4ddd028663d9221c83ca80 Mon Sep 17 00:00:00 2001 From: Eric Fahlgren Date: Thu, 22 Jan 2026 16:12:33 -0800 Subject: [PATCH] luci-app-attendedsysupgrade: rework check-for-new-firmware Do a complete overhaul of the firmware check: When the Status -> Overview page is loaded, we check if the configuration is set. If not, we ask the user to choose between enabled or disabled. Once this is done, it never appears again (much like the "set password" logic in the shell). As a result, there is no longer a persistent section on the Overview page with a simple toggle eating real estate and playing havoc with the Hide/Show button scheme. When the setting is enabled, then every time Status -> Overview is loaded, we do the firmware check and display an alert notice as before. But, the alert notice now contains a button to disable the alerts, so navigation is still simple, you don't have to dig around to figure out how to turn it off. The logic for version comparisons was cleaned up and simplified. Fixes: #7925 Fixes: #8226 Signed-off-by: Eric Fahlgren --- .../view/attendedsysupgrade/configuration.js | 16 ++- .../view/status/include/11_upgrades.js | 111 +++++++++++------- ...5-luci-app-attendedsysupgrade-housekeeping | 9 ++ 3 files changed, 87 insertions(+), 49 deletions(-) create mode 100755 applications/luci-app-attendedsysupgrade/root/etc/uci-defaults/95-luci-app-attendedsysupgrade-housekeeping diff --git a/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/configuration.js b/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/configuration.js index d5d1ddfbf4..5dcfe68d97 100644 --- a/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/configuration.js +++ b/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/configuration.js @@ -26,20 +26,28 @@ return view.extend({ form.DynamicList, 'rebuilder', _('Rebuilders'), - _( - 'Other ASU server instances that rebuild a requested image. ' + - 'Allows to compare checksums and verify that the results are the same.' + _('Other ASU server instances that rebuild a requested image. ' + + 'Allows to compare checksums and verify that the results are the same.' ) ); s = m.section(form.TypedSection, 'client', _('Client')); s.anonymous = true; + o = s.option( + form.Flag, + 'login_check_for_upgrades', + _('Check for upgrades'), + _('Check for upgrades whenever the System -> Overview page is loaded.') + ); + o.default = '0'; + o.rmempty = false; + o = s.option( form.Flag, 'auto_search', _('Search on opening'), - _('Search for new sysupgrades on opening the tab') + _('Search for upgrades when opening the Attended Sysupgrade tab') ); o.default = '1'; o.rmempty = false; diff --git a/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/status/include/11_upgrades.js b/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/status/include/11_upgrades.js index aefc80a25c..c4edcad158 100644 --- a/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/status/include/11_upgrades.js +++ b/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/status/include/11_upgrades.js @@ -11,13 +11,25 @@ const callSystemBoard = rpc.declare({ method: 'board' }); -function showUpgradeNotification(type, boardinfo, new_version, upgrade_info) +const check_setting = [ 'attendedsysupgrade', 'client', 'login_check_for_upgrades' ]; + +function setSetUpgradeCheck(pref) { + uci.set(...check_setting, pref); + return uci.save() + .then(L.bind(L.ui.changes.init, L.ui.changes)) + .then(L.bind(L.ui.changes.displayChanges, L.ui.changes)); +} + +function showUpgradeNotification(type, boardinfo, version, upgrade_info) { + + // TODO show the toggle for _('Look online for upgrades upon status page load')... + const table_rows = [ // Title Current Available [_('Firmware Version'), boardinfo.release.version, upgrade_info.version_number ], [_('Revision'), boardinfo.release.revision, upgrade_info.version_code ], - [_('Kernel Version'), boardinfo?.kernel, upgrade_info.linux_kernel?.version ], + [_('Kernel Version'), boardinfo.kernel, upgrade_info.linux_kernel?.version ], ]; const table = E('table', { 'class': 'table' }); @@ -30,54 +42,55 @@ function showUpgradeNotification(type, boardinfo, new_version, upgrade_info) ]) ); - table_rows.forEach((cols) => { + table_rows.forEach(([c1, c2, c3]) => { table.appendChild(E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td left', 'width': '33%' }, [ cols[0] ]), - E('td', { 'class': 'td left' }, [ cols[1] ?? '?' ]), - E('td', { 'class': 'td left' }, [ cols[2] ?? '?' ]), + E('td', { 'class': 'td left', 'width': '33%' }, [ c1 ]), + E('td', { 'class': 'td left' }, [ c2 ?? '?' ]), + E('td', { 'class': 'td left' }, [ c3 ?? '?' ]), ])); }); + const branch = version.split('.').slice(0, 2).join('.'); ui.addTimeLimitedNotification(_('New Firmware Available'), [ E('p', _('A new %s version of OpenWrt is available:').format(type)), table, E('p', [ _('Check') + ' ', - E('a', {href: `/cgi-bin/luci/admin/system/attendedsysupgrade`}, _('Attended Sysupgrade')), + E('a', {href: '/cgi-bin/luci/admin/system/attendedsysupgrade'}, _('Attended Sysupgrade')), ' ' + _('and') + ' ', - E('a', {href: `https://openwrt.org/releases/${new_version?.split('.').slice(0, 2).join('.')}/notes-${new_version}`}, _('release notes')), + E('a', {href: `https://openwrt.org/releases/${branch}/notes-${version}`}, _('release notes')), ]), + E('div', { class: 'btn', click: () => setSetUpgradeCheck(false) }, _('Stop showing upgrade alerts')), ], 60000, 'notice'); -}; +} function shouldUpgrade(installed, available) { // If installed is any snapshot (release or main), don't upgrade. if (! available) return false; - if (! installed || installed.includes('SNAPSHOT')) return false + if (! installed) return false; + if (installed.includes('SNAPSHOT')) return false; - const parse = (v) => v.split(/[-+]/)[0]?.split('.').map(Number); - const parseRC = (v) => v.split(/[-+]/)[1]?.split('').map(Number); - const isPrerelease = (v) => /-/.test(v); + // At this point we know the versions are in one of two forms: + // MM.mm.rr + // MM.mm.rr-rcN + // so partition them up into a 4-element array, with a value of + // 99 for the "release candidate" part of any release. + const parse = (v) => [ + ...v.split('-')[0].split('.').map(Number), + Number(v.split(/rc/)[1] || 99) + ]; - const [aParts, bParts] = [parse(available), parse(installed)]; + const [aParts, iParts] = [parse(available), parse(installed)]; - for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) { - const numA = aParts[i] || 0; - const numB = bParts[i] || 0; - if (numA > numB) return true; - if (numA < numB) return false; + for (let i = 0; i < iParts.length; i++) { + const aVal = aParts[i]; + const iVal = iParts[i]; + if (aVal > iVal) return true; + if (aVal < iVal) return false; } - const [aRC, bRC] = [parseRC(available), parseRC(installed)]; - - if (aRC > bRC) return true; - if (aRC < bRC) return false; - - // If numeric parts are equal, handle release candidates - // if (isPrerelease(available) && !isPrerelease(installed)) return false; - if (!isPrerelease(available) && isPrerelease(installed)) return true; return false; } @@ -116,24 +129,14 @@ return baseclass.extend({ load: function() { return Promise.all([ L.resolveDefault(callSystemBoard(), {}), - uci.load('luci') + uci.load(check_setting[0]), ]); }, - handleSetUpgradeCheck: function(pref, ev) { - ev.currentTarget.classList.add('spinning'); - ev.currentTarget.blur(); - - uci.set('luci', 'main', 'check_for_newer_firmwares', pref); - - return uci.save() - .then(L.bind(L.ui.changes.init, L.ui.changes)) - .then(L.bind(L.ui.changes.displayChanges, L.ui.changes)); - }, oneshot: function(data) { var boardinfo = data[0]; - const check_upgrades = uci.get_bool('luci', 'main', 'check_for_newer_firmwares'); + const check_upgrades = uci.get_bool(...check_setting); if (check_upgrades) { fetch('https://downloads.openwrt.org/.versions.json') @@ -177,14 +180,32 @@ return baseclass.extend({ }, render: function(data) { - const check_upgrades = uci.get_bool('luci', 'main', 'check_for_newer_firmwares') ?? false; - const isReadonlyView = !L.hasViewPermission(); + const isReadOnlyView = !L.hasViewPermission(); + if (isReadOnlyView) + return null; - let perform_check_pref = E('input', { type: 'checkbox', 'click': L.bind(this.handleSetUpgradeCheck, this, !check_upgrades), }); - perform_check_pref.checked = check_upgrades; + let check_upgrades = uci.get(...check_setting); + if (check_upgrades != null) + return null; - let perform_check_pref_p = E('div', [_('Look online for upgrades upon status page load') + ' ', perform_check_pref]); + let modal_body = [ + E('p', _('Checking for firmware upgrades requires access to several files ' + + 'on the downloads site, so requires internet access.')), - return E('div', [!isReadonlyView ? perform_check_pref_p : '']); + E('p', _('The check will be performed every time the Status -> Overview page is loaded.')), + + E('p', _('You have not yet specified a preference for this setting. ' + + 'Once set, this dialog will not be shown again, but you can go to ' + + 'System -> Attended Sysupgrade configuration to change the setting.')), + + E('div', { class: 'right' }, [ + E('div', { class: 'btn', click: () => setSetUpgradeCheck(true) }, _('Yes, enable checking')), + E('div', { class: 'btn', click: () => setSetUpgradeCheck(false) }, _('No, disable checking')), + E('div', { class: 'btn', click: ui.hideModal }, _('Close')), + ]), + ]; + ui.showModal(_('Check online for firmware upgrades'), modal_body); + + return null; } }); diff --git a/applications/luci-app-attendedsysupgrade/root/etc/uci-defaults/95-luci-app-attendedsysupgrade-housekeeping b/applications/luci-app-attendedsysupgrade/root/etc/uci-defaults/95-luci-app-attendedsysupgrade-housekeeping new file mode 100755 index 0000000000..5ad127b820 --- /dev/null +++ b/applications/luci-app-attendedsysupgrade/root/etc/uci-defaults/95-luci-app-attendedsysupgrade-housekeeping @@ -0,0 +1,9 @@ +#!/bin/sh +check="$(uci -q get luci.main.check_for_newer_firmwares)" +if [ -n "$check" ]; then + if ! uci -q get attendedsysupgrade.client.login_check_for_upgrades >/dev/null; then + uci set attendedsysupgrade.client.login_check_for_upgrades="$check" + fi + uci -q delete luci.main.check_for_newer_firmwares +fi +exit 0