Skip to content

luci-app-package-manager: use apk's upgradable package list - #9020

Open
terrytyc wants to merge 1 commit into
openwrt:masterfrom
terrytyc:fix/apk-upgradable-list
Open

terrytyc wants to merge 1 commit into
openwrt:masterfrom
terrytyc:fix/apk-upgradable-list

Conversation

@terrytyc

@terrytyc terrytyc commented Sep 11, 2026 •

Copy link
Copy Markdown

Pull request details

Description

Use apk list -u as the source of APK upgrade candidates instead of inferring upgrades from the available-package map with the JavaScript version comparator.

The Updates tab can disagree with the command line: in the reported case LuCI shows four updates while apk list -u lists only appfilter and base-files. A kernel hash is compared as numeric version components, and a package installed from a tagged repository is compared against a different repository's version.

This change:

  • Adds a read-only list-upgradable wrapper action and its exact RPC file ACL entry.
  • Uses the native list for the Updates tab and the upgrade buttons on the Available tab.
  • Uses the selected candidate consistently for row versions, sizes, descriptions, filtering and upgrade details, reusing metadata only when both name and version match. Missing candidate metadata is displayed as unknown rather than borrowed from another version.
  • Refreshes the candidate list with the existing package lists. Unrecognized output lines are skipped with one incomplete-list warning per page load, so valid rows and install/remove controls remain usable.
  • Leaves opkg behavior, dependency comparison, repositories and package operations unchanged.

apk list -u is intentional: this lists available upgrades, not an apk upgrade --simulate transaction. Execution-time constraints remain the package manager's responsibility.

Originally submitted as immortalwrt/luci#709; submitted upstream at an ImmortalWrt maintainer's request. This PR is based directly on OpenWrt LuCI master 289a7260434d4b8212a9cc6cf6160cb1935270c1, without downstream-only commits.

Tests

Run the dependency-free regression check:

sh applications/luci-app-package-manager/tests/run_tests.sh

A dedicated .github/workflows/package-manager.yml workflow invokes this runner on Node.js 24, only for changes to applications/luci-app-package-manager/** or the workflow itself. It runs independently of ESLint; the ESLint workflow is unchanged by this PR. The test is named .js so it remains included in ESLint's existing lint filter. The harness executes the complete, unmodified view source in a function instead of rewriting an exact return view.extend source string.

It covers the four-versus-two discrepancy, tagged candidate metadata/version selection, Available-tab buttons, upgrade details, numeric/hyphenated names, revisions, duplicate candidates, empty-list refreshes, mixed valid/invalid and entirely invalid output, incomplete-list warning deduplication across refreshes and after dismissal, warning reset on a fresh view load, missing candidate metadata, command failure propagation, the read ACL and unchanged opkg behavior.

  • The regression check passed against this OpenWrt-based patch in Node.js 24.
  • Repository ESLint (ESLint 9, as used by the workflow) passed for the changed JavaScript and JSON files.
  • JavaScript syntax checks, shell syntax check and git diff --check passed.

Tested on

  • ImmortalWrt 25.12.2, x86_64, apk-tools 3.0.5, LuCI openwrt-25.12 26.236.50544~cb5d434: equivalent ports of the initial list-alignment change and the first review revision (4d306187) were deployed and verified in Microsoft Edge. After reloading the frontend script, the Updates tab showed only appfilter and base-files, matching apk list -u.
  • The exact OpenWrt-master-based patch has been tested locally with the regression check and lint/syntax checks above; it has not been built into or deployed as a stock OpenWrt firmware image. The latest warning-deduplication and workflow-isolation changes have been verified locally; the once-per-page warning change has not been redeployed to the router.

Checklist

  • Separate feature branch with one signed-off commit; no merge commits.
  • No new runtime dependencies; a dedicated path-filtered workflow runs the package-manager regression check independently of ESLint.
  • No explicit PKG_VERSION or PKG_RELEASE is defined in this package's Makefile to increment.

@openwrt openwrt Bot added the not following guidelines Pull request does not follow formatting guidelines label Sep 11, 2026
@terrytyc
terrytyc force-pushed the fix/apk-upgradable-list branch from a0402b9 to b555d94 Compare September 11, 2026 10:06
@openwrt openwrt Bot removed the not following guidelines Pull request does not follow formatting guidelines label Sep 11, 2026
Comment on lines +271 to +272
if (!m)
throw new Error(_('Unable to parse the list of upgradable packages.'));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing here rejects updateLists(), and nothing catches that rejection — updateLists(listData) at package-manager.js:1387`` runs inside requestAnimationFrame with no handler — so one odd line leaves the whole Software page stuck on the loading spinner, with Install/Remove unreachable too. `apk list -u` is human-readable output rather than a stable interface (unlike the `--format json` queries the other two actions use), so a provided/renamed package or an extra line is a realistic input. Degrade to skipping the line instead of killing the view.

Suggested change
if (!m)
throw new Error(_('Unable to parse the list of upgradable packages.'));
if (!m)
continue;

Generated by Claude Code

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 4d30618. Unrecognized lines are now skipped, and one notification per parse warns that the update list may be incomplete. Valid entries still render. The regression test covers mixed valid/invalid output and entirely invalid output, and verifies that install/remove controls remain usable.

;;
list-upgradable)
if [ $ipkg_bin = "apk" ]; then
$ipkg_bin list -u

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list-installed and list-available apk branches above both redirect stderr; this one doesn't, so apk's index/signature warnings land in the rpcd log on every refresh.

Suggested change
$ipkg_bin list -u
$ipkg_bin list -u 2>/dev/null

Generated by Claude Code

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 4d30618: added 2>/dev/null to the apk list -u branch, matching the other read-only queries. The command's exit status is preserved.

Comment on lines +360 to +362
const upgrade = L.hasSystemFeature('apk') ? packages.upgradable[name] : inst && inst.version !== pkg.version;

ver = truncateVersion(pkg.version || '-');
ver = truncateVersion((upgrade && upgrade.version) || pkg.version || '-');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • On the opkg path upgrade is a boolean, so upgrade && upgrade.version evaluates true.version and relies on it being undefined to fall through to pkg.version. Give the two paths separate variables, or keep upgrade a package object in both.
  • On the apk path the row now mixes sources: ver is apk's candidate version while the size and description columns below still come from packages.available.pkgs[name], which is exactly the other-repository candidate this PR set out to stop trusting.

Generated by Claude Code

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 4d30618. upgrade is now used only as a condition, with no property access on an opkg boolean. APK rows use the selected candidate consistently for version, size, description and filtering on both Updates and Available tabs. Missing candidate metadata stays unknown instead of falling back to another version. Installed-tab and opkg behavior are preserved. Added regression coverage for these cases.

@@ -0,0 +1,110 @@
'use strict';

// Run with: node applications/luci-app-package-manager/tests/apk-upgradable.cjs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: nothing runs this — no workflow invokes it, and eslint.yml's filter is \.(js|json|md)$, so a .cjs file isn't even linted. Combined with the harness depending on the exact source string \nreturn view.extend({, it will silently rot on the next refactor of the view. The other two tests/ dirs in the tree ship a run_tests.sh; is there a runner planned for this one?


Generated by Claude Code

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 4d30618. Added tests/run_tests.sh and a step invoking it in the existing ESLint workflow, reusing its Node setup. Renamed the test to .js so the current lint filter includes it. The harness now executes the complete, unmodified view source with vm.compileFunction() instead of matching/replacing the exact return view.extend text, and it exercises the actual pagination/table-rendering path. The runner and repository ESLint both pass locally; no new dependencies were added.

@terrytyc
terrytyc force-pushed the fix/apk-upgradable-list branch from b555d94 to 4d30618 Compare September 14, 2026 07:22
@1715173329

Copy link
Copy Markdown
Member

Cc: @systemcrash

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit.


Generated by Claude Code

}

if (skipped)
ui.addNotification(null, E('p', {}, _('Some upgrade entries could not be parsed. The update list may be incomplete.')), 'warning');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: updateLists() runs again after every install/remove/upgrade,`` so a system with one persistently unparseable line gets a fresh banner per operation and LuCI notifications stack until dismissed. Consider raising it once per page load (module-level flag) or logging to the console instead.


Generated by Claude Code

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in cb0cff6. A module-level guard now limits this warning to once per page load; updateLists() does not reset it. Regression coverage verifies repeated refreshes, dismissal followed by valid/invalid refreshes, and that loading a fresh view allows the warning again.

Comment thread .github/workflows/eslint.yml Outdated
Comment on lines +35 to +36
- name: Test package manager
run: sh applications/luci-app-package-manager/tests/run_tests.sh

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this step is unscoped and sits before the lint steps, so a failure in one app's test turns the repo-wide ESLint check red on unrelated PRs and skips the lint output they came for. A separate job (here or in its own workflow) keeps the two results independent.


Generated by Claude Code

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in cb0cff6. Moved the runner into a dedicated package-manager.yml workflow with path filters for this package and the workflow itself. It has no dependency on ESLint and uses Node.js 24 with read-only repository permissions. The existing ESLint workflow is now unchanged by this PR, so a package-manager test failure no longer suppresses lint output or runs on unrelated-only changes. The runner, repository ESLint and local workflow/path-filter checks pass.

@terrytyc
terrytyc force-pushed the fix/apk-upgradable-list branch from 4d30618 to cb0cff6 Compare September 15, 2026 05:10

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit; no new issues found.


Generated by Claude Code

@terrytyc
terrytyc force-pushed the fix/apk-upgradable-list branch from cb0cff6 to 604fbab Compare September 16, 2026 01:16
@terrytyc
terrytyc requested a review from openwrt-ai September 16, 2026 01:21

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit; no new issues found.


Generated by Claude Code

@efahl

efahl commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

If you're going to scrape apk text output, you should consider using apk version as it is much more easily parsed and gives exactly what you're looking for.

$ apk version
Installed:                                         Available:
libopenssl-conf-3.5.8-r2                         < 3.5.8-r3
libopenssl3-3.5.8-r2                             < 3.5.8-r3
libusb-1.0-0-1.0.29-r1                           < 1.0.29-r2
luci-26.256.49187~22e4e76                        < 26.257.65361~d19bf13
luci-app-attendedsysupgrade-26.256.49187~22e4e76 < 26.257.65361~d19bf13
luci-app-firewall-26.256.49187~22e4e76           < 26.257.65361~d19bf13
...

@openwrt openwrt Bot added the not following guidelines Pull request does not follow formatting guidelines label Oct 9, 2026
Read apk list -u through the package manager wrapper instead of inferring
APK upgrades from the available version with the JavaScript comparator.
This avoids false upgrades for hash-based versions and packages installed
from tagged repositories.

Use the native list for both the Updates tab and available-package upgrade
buttons. Use matching candidate metadata for versions, sizes, descriptions
and details. Skip unrecognized output lines and warn once per page load
instead of rejecting the page refresh. Keep opkg and package operations
unchanged.

Add a dependency-free Node regression check with a shell runner and a
path-filtered workflow independent of ESLint. Cover filtering, candidate
metadata, malformed output, warning deduplication, refreshes, command
failures and the opkg path.

Signed-off-by: Terry Tang <terrytyc@gmail.com>
@terrytyc
terrytyc force-pushed the fix/apk-upgradable-list branch from dadc9d6 to 7e19655 Compare October 9, 2026 02:05
@openwrt openwrt Bot removed the not following guidelines Pull request does not follow formatting guidelines label Oct 9, 2026

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit; no new issues found.


Generated by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants