diff mbox series

[V2,1/4] recipeutils: add optional stable_upgrade parameter to get_recipe_upgrade_status

Message ID 20260507062010.1461027-1-Qi.Chen@windriver.com
State Under Review
Headers show
Series [V2,1/4] recipeutils: add optional stable_upgrade parameter to get_recipe_upgrade_status | expand

Commit Message

Chen, Qi May 7, 2026, 6:20 a.m. UTC
From: Chen Qi <Qi.Chen@windriver.com>

We want the ability to do stable version upgrades for recipes.
To this end, add an optional stable_upgrade parameter to the
get_recipe_upgrade_status function, which defaults to False and
when enabled will try to get the latest stable version of the recipe.

The UPSTREAM_STABLE_RELEASE_REGEX is respected. If a recipe sets
it, it will be used as the filter_regex. If it's not set, we give
it a reasonable default value, which means if a version consists of
at least three parts, the bump in last one is considered as a stable
upgrade. If a version consists of less then three parts, we'd be
conservative to treat any bump as a non-stable upgrade (e.g., strace,
coreutils).

Recipes such as kmod and systemd have versions consisting of less
than three parts. So they'll need to set UPSTREAM_STABLE_RELEASE_REGEX
in meta data.

Signed-off-by: Chen Qi <Qi.Chen@windriver.com>
---
 meta/lib/oe/recipeutils.py | 27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)

Comments

Alexander Kanavin May 7, 2026, 11:33 a.m. UTC | #1
On Thu, 7 May 2026 at 08:20, <Qi.Chen@windriver.com> wrote:
> If it's not set, we give
> it a reasonable default value, which means if a version consists of
> at least three parts, the bump in last one is considered as a stable
> upgrade.

I strongly disagree and think we should be a lot more careful. This
default will result in a lot of point version updates where the LTS
maintainer would have to go and check whether the updates include new
features or not. Yoann already had to reject a bunch of such updates,
produced in a similar way with custom AUH tweaks.

Rather this should be enabled on a case by case basis for components
that are known to do stable maintenance (a good indicator is presence
of stable branches with a major.minor version in branch name
somewhere), by setting UPSTREAM_STABLE_RELEASE_REGEX for them (perhaps
via class inherit to avoid repetition).

No explicitly set UPSTREAM_STABLE_RELEASE_REGEX means no stable
updates exist, or the situation hasn't yet been checked.

Alex
Chen, Qi May 8, 2026, 2:36 a.m. UTC | #2
Got it. I see your rational and I now understand why you proposed a bbclass.
I'll use this bbclass method and send out V3.

Regards,
Qi

-----Original Message-----
From: Alexander Kanavin <alex.kanavin@gmail.com> 
Sent: Thursday, May 7, 2026 7:33 PM
To: Chen, Qi <Qi.Chen@windriver.com>
Cc: openembedded-core@lists.openembedded.org; MacLeod, Randy <Randy.MacLeod@windriver.com>
Subject: Re: [OE-core][PATCH V2 1/4] recipeutils: add optional stable_upgrade parameter to get_recipe_upgrade_status

On Thu, 7 May 2026 at 08:20, <Qi.Chen@windriver.com> wrote:
> If it's not set, we give
> it a reasonable default value, which means if a version consists of at 
> least three parts, the bump in last one is considered as a stable 
> upgrade.

I strongly disagree and think we should be a lot more careful. This default will result in a lot of point version updates where the LTS maintainer would have to go and check whether the updates include new features or not. Yoann already had to reject a bunch of such updates, produced in a similar way with custom AUH tweaks.

Rather this should be enabled on a case by case basis for components that are known to do stable maintenance (a good indicator is presence of stable branches with a major.minor version in branch name somewhere), by setting UPSTREAM_STABLE_RELEASE_REGEX for them (perhaps via class inherit to avoid repetition).

No explicitly set UPSTREAM_STABLE_RELEASE_REGEX means no stable updates exist, or the situation hasn't yet been checked.

Alex
diff mbox series

Patch

diff --git a/meta/lib/oe/recipeutils.py b/meta/lib/oe/recipeutils.py
index c6604f536d..3fc427fa5e 100644
--- a/meta/lib/oe/recipeutils.py
+++ b/meta/lib/oe/recipeutils.py
@@ -1009,7 +1009,7 @@  def get_recipe_pv_with_pfx_sfx(pv, uri_type):
 
     return (pv, pfx, sfx)
 
-def get_recipe_upstream_version(rd):
+def get_recipe_upstream_version(rd, stable_upgrade):
     """
         Get upstream version of recipe using bb.fetch2 methods with support for
         http, https, ftp and git.
@@ -1080,7 +1080,19 @@  def get_recipe_upstream_version(rd):
             except bb.fetch2.FetchError as e:
                 bb.warn("Unable to obtain latest revision: {}".format(e))
         else:
-            pupver = ud.method.latest_versionstring(ud, rd)
+            if stable_upgrade:
+                stable_release_regex = rd.getVar("UPSTREAM_STABLE_RELEASE_REGEX")
+                if stable_release_regex:
+                    pupver = ud.method.latest_versionstring(ud, rd, filter_regex=stable_release_regex)
+                else:
+                    # Recipe not setting UPSTREAM_STABLE_RELEASE_REGEX, give it some reasonable default.
+                    if len(ru['current_version'].split('.')) >= 3:
+                        stable_release_regex = '%s\.\d' % '\.'.join(ru['current_version'].split('.')[:-1])
+                        pupver = ud.method.latest_versionstring(ud, rd, filter_regex=stable_release_regex)
+                    else:
+                        pupver = (ru['current_version'], None)
+            else:
+                pupver = ud.method.latest_versionstring(ud, rd)
             (upversion, revision) = pupver
 
         if upversion:
@@ -1094,8 +1106,8 @@  def get_recipe_upstream_version(rd):
 
     return ru
 
-def _get_recipe_upgrade_status(data):
-    uv = get_recipe_upstream_version(data)
+def _get_recipe_upgrade_status(data, stable_upgrade):
+    uv = get_recipe_upstream_version(data, stable_upgrade)
 
     pn = data.getVar('PN')
     cur_ver = uv['current_version']
@@ -1119,9 +1131,10 @@  def _get_recipe_upgrade_status(data):
 
     return {'pn':pn, 'status':status, 'cur_ver':cur_ver, 'next_ver':next_ver, 'maintainer':maintainer, 'revision':revision, 'no_upgrade_reason':no_upgrade_reason}
 
-def get_recipe_upgrade_status(recipes=None):
+def get_recipe_upgrade_status(recipes=None, stable_upgrade=False):
     pkgs_list = []
     data_copy_list = []
+    stable_copy_list = []
     copy_vars = ('SRC_URI',
                  'PV',
                  'DL_DIR',
@@ -1134,6 +1147,7 @@  def get_recipe_upgrade_status(recipes=None):
                  'UPSTREAM_CHECK_REGEX',
                  'UPSTREAM_CHECK_URI',
                  'UPSTREAM_VERSION_UNKNOWN',
+                 'UPSTREAM_STABLE_RELEASE_REGEX',
                  'RECIPE_MAINTAINER',
                  'RECIPE_NO_UPDATE_REASON',
                  'RECIPE_UPSTREAM_VERSION',
@@ -1180,12 +1194,13 @@  def get_recipe_upgrade_status(recipes=None):
                     data_copy.setVar(k, data.getVar(k))
 
             data_copy_list.append(data_copy)
+            stable_copy_list.append(stable_upgrade)
 
             recipeincludes[data.getVar('FILE')] = {'bbincluded':data.getVar('BBINCLUDED').split(),'pn':data.getVar('PN')}
 
     from concurrent.futures import ProcessPoolExecutor
     with ProcessPoolExecutor(max_workers=utils.cpu_count()) as executor:
-        pkgs_list = executor.map(_get_recipe_upgrade_status, data_copy_list)
+        pkgs_list = executor.map(_get_recipe_upgrade_status, data_copy_list, stable_copy_list)
 
     return _group_recipes(pkgs_list, _get_common_include_recipes(recipeincludes))