From a4d5a31cb34f795dc4dbdd622afd05083707fb60 Mon Sep 17 00:00:00 2001 From: Lucas Holt Date: Thu, 2 Jul 2026 21:16:47 -0400 Subject: [PATCH 1/2] libmport: update local package on offline install when file is newer For offline/local-file installs, if the same package is already installed on the current OS release and the local .mport file carries a newer version, remove the installed copy and install the file (an in-place update), preserving the previous automatic flag. Without --force, a same-or-older file still reports "already installed" as before. Adds lookup_current_os_installed(), which only matches an installed package whose os_release equals the running system's, so packages from a different OS release are not treated as version bumps. Deletes the actually-installed meta (not the bundle stub) before the conflict and installed prechecks. Documents the behavior in mport.1. AI-Assisted-By: Claude Opus 4.8 (1M context) Co-Authored-By: Claude Opus 4.8 (1M context) --- libmport/install_primative.c | 56 +++++++++++++++++++++++++++++++----- mport/mport.1 | 4 +++ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/libmport/install_primative.c b/libmport/install_primative.c index f63e674..05475c6 100644 --- a/libmport/install_primative.c +++ b/libmport/install_primative.c @@ -37,6 +37,8 @@ static char **get_dependencies(mportInstance *mport, mportPackageMeta *pack); static char *find_file_with_prefix(const char *dir, const char *prefix); +static /*@only@*/ mportPackageMeta **lookup_current_os_installed( + /*@notnull@*/ mportInstance *, /*@notnull@*/ mportPackageMeta *); #define GOTO_CLEANUP_ON_MPORT_ERR(expr) \ do { \ @@ -144,6 +146,33 @@ find_file_with_prefix(const char *dir, const char *prefix) return found_file; } +static mportPackageMeta ** +lookup_current_os_installed(mportInstance *mport, mportPackageMeta *pkg) +{ + mportPackageMeta **installed = NULL; + char *system_os_release; + + if (mport_pkgmeta_search_master(mport, &installed, "pkg=%Q", pkg->name) != MPORT_OK) + return NULL; + if (installed == NULL) + return NULL; + if (installed[0] == NULL) { + mport_pkgmeta_vec_free(installed); + return NULL; + } + + system_os_release = mport_get_osrelease(mport); + if (system_os_release == NULL || installed[0]->os_release == NULL || + strcmp(installed[0]->os_release, system_os_release) != 0) { + free(system_os_release); + mport_pkgmeta_vec_free(installed); + return NULL; + } + + free(system_os_release); + return installed; +} + MPORT_PUBLIC_API int mport_install_primative( mportInstance *mport, const char *filename, const char *prefix, mportAutomatic automatic) @@ -184,19 +213,32 @@ mport_install_primative( goto cleanup; } - /* if we previously installed it and want to force, allow it. - In this case, automatic flag from previous install not honored - */ - if (mport_check_preconditions(mport, pkgs[0], MPORT_PRECHECK_INSTALLED) != - MPORT_OK) { - if (mport->force) { - mport_delete_primative(mport, pkgs[0], 1); + already_installed = lookup_current_os_installed(mport, pkgs[0]); + if (already_installed != NULL && already_installed[0] != NULL) { + int version_cmp = + mport_version_cmp(already_installed[0]->version, pkgs[0]->version); + + if (mport->force || version_cmp < 0) { + automatic = already_installed[0]->automatic; + if (version_cmp < 0) { + mport_call_msg_cb(mport, "Updating %s from %s to %s.", + pkgs[0]->name, already_installed[0]->version, + pkgs[0]->version); + } + if (mport_delete_primative(mport, already_installed[0], 1) != + MPORT_OK) { + ret = mport_err_code(); + goto cleanup; + } } else { mport_call_msg_cb(mport, "%s-%s: already installed.", pkgs[0]->name, pkgs[0]->version); ret = MPORT_OK; goto cleanup; } + + mport_pkgmeta_vec_free(already_installed); + already_installed = NULL; } if (mport_check_preconditions(mport, pkgs[0], MPORT_PRECHECK_CONFLICTS) != diff --git a/mport/mport.1 b/mport/mport.1 index e890845..4ce9413 100644 --- a/mport/mport.1 +++ b/mport/mport.1 @@ -247,6 +247,10 @@ package files. If .Fl A is specified, mark the installed package as automatic. +If the package is already installed and the local package file contains a newer +version, +.Nm +updates it by removing the installed package and installing the local file. For repository installs, use .Cm install . .It Cm annotate Op Fl a Op Fl q Fl S Ar package Ar tag From 02c537aaef2ac75ca151e38f819926cf621949d4 Mon Sep 17 00:00:00 2001 From: Lucas Holt Date: Thu, 2 Jul 2026 21:25:31 -0400 Subject: [PATCH 2/2] =?UTF-8?q?libmport:=20address=20review=20=E2=80=94=20?= =?UTF-8?q?scan=20all=20installed=20rows=20for=20current-OS=20match?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit packages.pkg is not unique in the master DB (the same name may be installed under different os_release values) and the search has no ORDER BY, so the current-OS entry is not necessarily row 0. Iterate the returned vector in lookup_current_os_installed() and return the matching entry via an out-parameter instead of only inspecting [0]. Document that cross-OS-release copies are intentionally left to the normal install path, where MPORT_PRECHECK_INSTALLED treats a differing os_release as a distinct package — this preserves the pre-existing --force behavior for that case (unchanged by this feature). AI-Assisted-By: Claude Opus 4.8 (1M context) Co-Authored-By: Claude Opus 4.8 (1M context) --- libmport/install_primative.c | 59 +++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/libmport/install_primative.c b/libmport/install_primative.c index 05475c6..b248184 100644 --- a/libmport/install_primative.c +++ b/libmport/install_primative.c @@ -37,8 +37,9 @@ static char **get_dependencies(mportInstance *mport, mportPackageMeta *pack); static char *find_file_with_prefix(const char *dir, const char *prefix); -static /*@only@*/ mportPackageMeta **lookup_current_os_installed( - /*@notnull@*/ mportInstance *, /*@notnull@*/ mportPackageMeta *); +static /*@null@*/ /*@only@*/ mportPackageMeta **lookup_current_os_installed( + /*@notnull@*/ mportInstance *, /*@notnull@*/ mportPackageMeta *, + /*@out@*/ mportPackageMeta **); #define GOTO_CLEANUP_ON_MPORT_ERR(expr) \ do { \ @@ -146,30 +147,50 @@ find_file_with_prefix(const char *dir, const char *prefix) return found_file; } +/* + * Return the installed package(s) matching pkg->name, and set *match to the + * single entry whose os_release equals the running system's. The package name + * is not unique in the master DB (the same name can be installed under a + * different os_release), so scan every returned row rather than assuming the + * current-OS entry is first. Returns NULL (and sets *match to NULL) when the + * package is not installed under the current OS release; the caller owns the + * returned vector, and *match is an observer into it. + */ static mportPackageMeta ** -lookup_current_os_installed(mportInstance *mport, mportPackageMeta *pkg) +lookup_current_os_installed(mportInstance *mport, mportPackageMeta *pkg, mportPackageMeta **match) { mportPackageMeta **installed = NULL; char *system_os_release; + int i; + + *match = NULL; if (mport_pkgmeta_search_master(mport, &installed, "pkg=%Q", pkg->name) != MPORT_OK) return NULL; if (installed == NULL) return NULL; - if (installed[0] == NULL) { + + system_os_release = mport_get_osrelease(mport); + if (system_os_release == NULL) { mport_pkgmeta_vec_free(installed); return NULL; } - system_os_release = mport_get_osrelease(mport); - if (system_os_release == NULL || installed[0]->os_release == NULL || - strcmp(installed[0]->os_release, system_os_release) != 0) { - free(system_os_release); + for (i = 0; installed[i] != NULL; i++) { + if (installed[i]->os_release != NULL && + strcmp(installed[i]->os_release, system_os_release) == 0) { + *match = installed[i]; + break; + } + } + + free(system_os_release); + + if (*match == NULL) { mport_pkgmeta_vec_free(installed); return NULL; } - free(system_os_release); return installed; } @@ -179,6 +200,7 @@ mport_install_primative( { mportBundleRead *bundle = NULL; mportPackageMeta **already_installed = NULL; + mportPackageMeta *current_installed = NULL; mportPackageMeta **pkgs = NULL; mportPackageMeta *pkg = NULL; int i; @@ -213,19 +235,26 @@ mport_install_primative( goto cleanup; } - already_installed = lookup_current_os_installed(mport, pkgs[0]); - if (already_installed != NULL && already_installed[0] != NULL) { + /* + * Only packages installed under the current OS release are treated + * as update/reinstall candidates here. A copy installed under a + * different os_release is left for the normal install path below, + * where MPORT_PRECHECK_INSTALLED considers a differing os_release to + * be a distinct package (matching pre-existing --force behavior). + */ + already_installed = lookup_current_os_installed(mport, pkgs[0], ¤t_installed); + if (current_installed != NULL) { int version_cmp = - mport_version_cmp(already_installed[0]->version, pkgs[0]->version); + mport_version_cmp(current_installed->version, pkgs[0]->version); if (mport->force || version_cmp < 0) { - automatic = already_installed[0]->automatic; + automatic = current_installed->automatic; if (version_cmp < 0) { mport_call_msg_cb(mport, "Updating %s from %s to %s.", - pkgs[0]->name, already_installed[0]->version, + pkgs[0]->name, current_installed->version, pkgs[0]->version); } - if (mport_delete_primative(mport, already_installed[0], 1) != + if (mport_delete_primative(mport, current_installed, 1) != MPORT_OK) { ret = mport_err_code(); goto cleanup;