Openembedded Core Discussions
 help / color / mirror / Atom feed
* [PATCH v2 0/3] devtool: upgrade: improve changelog extraction for git-log-style ChangeLogs
@ 2026-08-04  6:02 daniel.turull
  2026-08-04  6:02 ` [PATCH v2 1/3] devtool: upgrade: ignore changelogs from 3rd party daniel.turull
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: daniel.turull @ 2026-08-04  6:02 UTC (permalink / raw)
  To: openembedded-core; +Cc: Daniel Turull

From: Daniel Turull <daniel.turull@ericsson.com>

devtool upgrade's changelog extraction misattributed unrelated commits
when upgrading recipes like nghttp2, whose ChangeLog is the literal output
of `git log`, regenerated wholesale on every release.

Changes from v1:
- Header-skipping in the structural diff now relies on the blank line
  git log always inserts before the commit message, instead of
  enumerating header prefixes with a regex.
- The UTF-8 decode fix is split into its own patch and applied to both
  git-show call sites instead of just one.
- A single-use helper that only sequenced two operations with no reuse
  benefit has been inlined into its caller.
- The git-diff fallback is now guarded against UnicodeDecodeError too,
  so a bad byte in the diff hunk itself doesn't crash extraction.

Daniel Turull (3):
  devtool: upgrade: ignore changelogs from 3rd party
  devtool: upgrade: read changelog blobs via a temp file
  devtool: upgrade: diff git-log-style changelogs by commit hash

 scripts/lib/devtool/upgrade.py | 90 +++++++++++++++++++++++++++++-----
 1 file changed, 79 insertions(+), 11 deletions(-)



^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 1/3] devtool: upgrade: ignore changelogs from 3rd party
  2026-08-04  6:02 [PATCH v2 0/3] devtool: upgrade: improve changelog extraction for git-log-style ChangeLogs daniel.turull
@ 2026-08-04  6:02 ` daniel.turull
  2026-08-04  6:02 ` [PATCH v2 2/3] devtool: upgrade: read changelog blobs via a temp file daniel.turull
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: daniel.turull @ 2026-08-04  6:02 UTC (permalink / raw)
  To: openembedded-core; +Cc: Daniel Turull

From: Daniel Turull <daniel.turull@ericsson.com>

Some upstream projects bundle vendored dependencies in their source
tree (e.g. nghttp2 ships third-party/mruby, which has its own
NEWS.md). The changelog extractor could mistake one of these
vendored changelogs for the recipe's own, misattributing unrelated
upstream changes to the package being upgraded.

Exclude paths under common vendoring directory names (third-party,
vendor, external, deps, etc.) from changelog candidates.

AI-Generated: Kiro with Claude Sonnet 5
Signed-off-by: Daniel Turull <daniel.turull@ericsson.com>
---
 scripts/lib/devtool/upgrade.py | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
index 13d51bf952..495a5b8217 100644
--- a/scripts/lib/devtool/upgrade.py
+++ b/scripts/lib/devtool/upgrade.py
@@ -55,6 +55,13 @@ _CHANGELOG_BASENAMES = {
     'perldelta.pod',
 }
 
+# Path components indicating a bundled/vendored dependency rather than the
+# recipe's own source, so its changelog-like files should not be mistaken
+# for the recipe's own changes (e.g. third-party/mruby/NEWS.md in nghttp2).
+_VENDORED_PATH_RE = re.compile(
+    r'(^|/)(third[-_]party|vendor|vendored|external|extern|deps|3rdparty)(/|$)',
+    re.IGNORECASE)
+
 def _run(cmd, cwd=''):
     logger.debug("Running command %s> %s" % (cwd,cmd))
     return bb.process.run('%s' % cmd, cwd=cwd)
@@ -591,6 +598,9 @@ def _extract_changelog(srctree, pn, old_ver, new_ver, old_tag, new_tag, workspac
     try:
         stdout, _ = _run('git diff --name-only %s %s' % (old_tag, new_tag), srctree)
         changed_files = [f.strip() for f in stdout.splitlines() if f.strip()]
+        # Exclude bundled/vendored dependencies; their changelogs are not
+        # relevant to this recipe's own version bump.
+        changed_files = [f for f in changed_files if not _VENDORED_PATH_RE.search(f)]
 
         # First pass: collect per-version release notes that changed
         # Matches files with a version number whose path suggests release notes


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 2/3] devtool: upgrade: read changelog blobs via a temp file
  2026-08-04  6:02 [PATCH v2 0/3] devtool: upgrade: improve changelog extraction for git-log-style ChangeLogs daniel.turull
  2026-08-04  6:02 ` [PATCH v2 1/3] devtool: upgrade: ignore changelogs from 3rd party daniel.turull
@ 2026-08-04  6:02 ` daniel.turull
  2026-08-04  6:02 ` [PATCH v2 3/3] devtool: upgrade: diff git-log-style changelogs by commit hash daniel.turull
       [not found] ` <18C884DEFF3686EE.1990914@lists.openembedded.org>
  3 siblings, 0 replies; 7+ messages in thread
From: daniel.turull @ 2026-08-04  6:02 UTC (permalink / raw)
  To: openembedded-core; +Cc: Daniel Turull

From: Daniel Turull <daniel.turull@ericsson.com>

bb.process.run() always decodes command output as UTF-8, which raises
UnicodeDecodeError for changelogs containing non-UTF-8 bytes (e.g.
Latin-1 author names). Add _git_show_file(), which redirects
`git show` to a temp file and reads it back with errors='replace' to
tolerate that, and use it for the existing per-version release notes
lookup in _extract_changelog(), which had the same crash exposure.

AI-Generated: Kiro with Claude Sonnet 5
Signed-off-by: Daniel Turull <daniel.turull@ericsson.com>
---
 scripts/lib/devtool/upgrade.py | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
index 495a5b8217..6883c99cce 100644
--- a/scripts/lib/devtool/upgrade.py
+++ b/scripts/lib/devtool/upgrade.py
@@ -589,6 +589,18 @@ def _resolve_rst_includes(content, srctree):
     return ''.join(result)
 
 
+def _git_show_file(srctree, ref, fname):
+    """Return the content of fname at ref. Changelogs occasionally contain
+    non-UTF-8 bytes (e.g. Latin-1 author names), which bb.process.run()
+    can't handle since it always decodes command output as UTF-8;
+    redirecting to a temp file and reading it back leniently avoids that
+    restriction."""
+    with tempfile.NamedTemporaryFile(prefix='devtool-changelog') as tmpf:
+        _run('git show %s > %s' % (shlex.quote('%s:%s' % (ref, fname)), shlex.quote(tmpf.name)), srctree)
+        with open(tmpf.name, 'r', errors='replace') as f:
+            return f.read()
+
+
 def _extract_changelog(srctree, pn, old_ver, new_ver, old_tag, new_tag, workspace_path, is_git_source):
     """Extract changelog between old and new version using devtool git tags."""
     changelog_content = None
@@ -612,10 +624,10 @@ def _extract_changelog(srctree, pn, old_ver, new_ver, old_tag, new_tag, workspac
                 continue
             if re.search(r'(releas|relnote|change|news|migrat)', fname, re.IGNORECASE):
                 try:
-                    file_content, _ = _run('git show %s' % shlex.quote('%s:%s' % (new_tag, fname)), srctree)
+                    file_content = _git_show_file(srctree, new_tag, fname)
                 except bb.process.ExecutionError:
                     try:
-                        file_content, _ = _run('git show %s' % shlex.quote('%s:%s' % (old_tag, fname)), srctree)
+                        file_content = _git_show_file(srctree, old_tag, fname)
                     except bb.process.ExecutionError:
                         continue
                 if file_content.strip():


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 3/3] devtool: upgrade: diff git-log-style changelogs by commit hash
  2026-08-04  6:02 [PATCH v2 0/3] devtool: upgrade: improve changelog extraction for git-log-style ChangeLogs daniel.turull
  2026-08-04  6:02 ` [PATCH v2 1/3] devtool: upgrade: ignore changelogs from 3rd party daniel.turull
  2026-08-04  6:02 ` [PATCH v2 2/3] devtool: upgrade: read changelog blobs via a temp file daniel.turull
@ 2026-08-04  6:02 ` daniel.turull
       [not found] ` <18C884DEFF3686EE.1990914@lists.openembedded.org>
  3 siblings, 0 replies; 7+ messages in thread
From: daniel.turull @ 2026-08-04  6:02 UTC (permalink / raw)
  To: openembedded-core; +Cc: Daniel Turull

From: Daniel Turull <daniel.turull@ericsson.com>

Files like nghttp2's ChangeLog are the literal output of `git log`,
regenerated wholesale on every release. New commits are prepended, so
every existing commit shifts position even though its content is
unchanged. A textual diff sees that shift as removed/added lines
instead of showing just the commits that were actually added.

Diff these files structurally instead: compare commit hashes between
the old and new file to tell moved commits from genuinely new ones,
and report the subject lines of the new commits. Falls back to the
existing line-based diff for changelogs that aren't git-log output.

AI-Generated: Kiro with Claude Sonnet 5
Signed-off-by: Daniel Turull <daniel.turull@ericsson.com>
---
 scripts/lib/devtool/upgrade.py | 64 +++++++++++++++++++++++++++++-----
 1 file changed, 55 insertions(+), 9 deletions(-)

diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
index 6883c99cce..856110c803 100644
--- a/scripts/lib/devtool/upgrade.py
+++ b/scripts/lib/devtool/upgrade.py
@@ -589,6 +589,43 @@ def _resolve_rst_includes(content, srctree):
     return ''.join(result)
 
 
+_GIT_LOG_COMMIT_RE = re.compile(r'^commit ([0-9a-f]{7,40})\b.*$', re.MULTILINE)
+
+def _diff_git_log_changelog(old_content, new_content):
+    """Diff a ChangeLog that is itself `git log` output (e.g. nghttp2),
+    regenerated wholesale on every release. New commits get prepended, so
+    old ones shift position even though unchanged, which a textual diff
+    would wrongly show as removed/added lines. Compare commit hashes
+    instead: hashes already in old_content are just moved, anything else
+    is new. Returns the new commits' subject lines as one string, or
+    None if either file doesn't look like git log output."""
+    old_hashes = set(_GIT_LOG_COMMIT_RE.findall(old_content))
+    new_commits = _GIT_LOG_COMMIT_RE.split(new_content)[1:]  # [hash, block, hash, block, ...]
+    if not old_hashes or not new_commits:
+        return None
+
+    subjects = []
+    for commit_hash, block in zip(new_commits[0::2], new_commits[1::2]):
+        if commit_hash in old_hashes:
+            continue
+        # `git log` separates headers (Author:, Date:, etc.) from the
+        # commit message with a blank line; drop the leading blank line
+        # left over from splitting on "commit <hash>", then skip the
+        # headers up to the next blank line to get just the message.
+        lines = block.splitlines()
+        while lines and not lines[0].strip():
+            lines.pop(0)
+        blank_at = next((i for i, l in enumerate(lines) if not l.strip()), len(lines))
+        message_lines = [l.strip() for l in lines[blank_at:] if l.strip()]
+        # Skip the 'Merge pull request ...' line GitHub adds as the first
+        # message line of a merge commit; the actual change subject is the
+        # next line, and keeping the merge line would duplicate it.
+        subject = next((l for l in message_lines if not l.startswith('Merge pull request ')), None)
+        if subject and (not subjects or subjects[-1] != subject):
+            subjects.append(subject)
+    return '\n'.join(subjects) if subjects else None
+
+
 def _git_show_file(srctree, ref, fname):
     """Return the content of fname at ref. Changelogs occasionally contain
     non-UTF-8 bytes (e.g. Latin-1 author names), which bb.process.run()
@@ -642,15 +679,24 @@ def _extract_changelog(srctree, pn, old_ver, new_ver, old_tag, new_tag, workspac
             for fname in changed_files:
                 basename = os.path.basename(fname).lower()
                 if basename in _CHANGELOG_BASENAMES:
-                    diff_out, _ = _run('git diff %s %s -- %s' % (old_tag, new_tag, shlex.quote(fname)), srctree)
-                    if diff_out.strip():
-                        lines = [line[1:] for line in diff_out.splitlines()
-                                 if line.startswith('+') and not line.startswith('+++')]
-                        if lines:
-                            candidate = '\n'.join(lines)
-                            if not changelog_content or len(candidate) > len(changelog_content):
-                                changelog_content = candidate
-                                changelog_fname = fname
+                    candidate = None
+                    try:
+                        old_file = _git_show_file(srctree, old_tag, fname)
+                        new_file = _git_show_file(srctree, new_tag, fname)
+                        candidate = _diff_git_log_changelog(old_file, new_file)
+                    except bb.process.ExecutionError:
+                        pass
+                    if candidate is None:
+                        try:
+                            diff_out, _ = _run('git diff %s %s -- %s' % (old_tag, new_tag, shlex.quote(fname)), srctree)
+                        except UnicodeDecodeError:
+                            diff_out = ''
+                        added_lines = [line[1:] for line in diff_out.splitlines()
+                                       if line.startswith('+') and not line.startswith('+++')]
+                        candidate = '\n'.join(added_lines) if added_lines else None
+                    if candidate and (not changelog_content or len(candidate) > len(changelog_content)):
+                        changelog_content = candidate
+                        changelog_fname = fname
     except bb.process.ExecutionError as e:
         logger.warning('Changelog file extraction failed: %s' % str(e))
 


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [OE-core] [PATCH v2 2/3] devtool: upgrade: read changelog blobs via a temp file
       [not found] ` <18C884DEFF3686EE.1990914@lists.openembedded.org>
@ 2026-08-04  6:06   ` Daniel Turull
  2026-08-06 14:05     ` Ross Burton
  0 siblings, 1 reply; 7+ messages in thread
From: Daniel Turull @ 2026-08-04  6:06 UTC (permalink / raw)
  To: openembedded-core@lists.openembedded.org,
	richard.purdie@linuxfoundation.org

On Tue, 2026-08-04 at 08:02 +0200, Daniel Turull via lists.openembedded.org wrote:
> From: Daniel Turull <daniel.turull@ericsson.com>
> 
> bb.process.run() always decodes command output as UTF-8, which raises
> UnicodeDecodeError for changelogs containing non-UTF-8 bytes (e.g.
> Latin-1 author names). Add _git_show_file(), which redirects
> `git show` to a temp file and reads it back with errors='replace' to
> tolerate that, and use it for the existing per-version release notes
> lookup in _extract_changelog(), which had the same crash exposure.
> 
> AI-Generated: Kiro with Claude Sonnet 5
> Signed-off-by: Daniel Turull <daniel.turull@ericsson.com>
> ---
>  scripts/lib/devtool/upgrade.py | 16 ++++++++++++++--
>  1 file changed, 14 insertions(+), 2 deletions(-)
> 
> diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
> index 495a5b8217..6883c99cce 100644
> --- a/scripts/lib/devtool/upgrade.py
> +++ b/scripts/lib/devtool/upgrade.py
> @@ -589,6 +589,18 @@ def _resolve_rst_includes(content, srctree):
>      return ''.join(result)
>  
>  
> +def _git_show_file(srctree, ref, fname):
> +    """Return the content of fname at ref. Changelogs occasionally contain
> +    non-UTF-8 bytes (e.g. Latin-1 author names), which bb.process.run()
> +    can't handle since it always decodes command output as UTF-8;
> +    redirecting to a temp file and reading it back leniently avoids that
> +    restriction."""
> +    with tempfile.NamedTemporaryFile(prefix='devtool-changelog') as tmpf:
> +        _run('git show %s > %s' % (shlex.quote('%s:%s' % (ref, fname)), shlex.quote(tmpf.name)),
> srctree)

The alternative to use a tmp file was to modify _run to allow other encodings than utf-8, but it
will be a bigger refactor. Or not use _run and call directly the raw subprocess calls.

I can change it to either option or keep the current patch.

> +        with open(tmpf.name, 'r', errors='replace') as f:
> +            return f.read()
> +
> +
> 

Daniel


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [OE-core] [PATCH v2 2/3] devtool: upgrade: read changelog blobs via a temp file
  2026-08-04  6:06   ` [OE-core] [PATCH v2 2/3] devtool: upgrade: read changelog blobs via a temp file Daniel Turull
@ 2026-08-06 14:05     ` Ross Burton
  2026-08-06 14:09       ` Daniel Turull
  0 siblings, 1 reply; 7+ messages in thread
From: Ross Burton @ 2026-08-06 14:05 UTC (permalink / raw)
  To: daniel.turull@ericsson.com
  Cc: openembedded-core@lists.openembedded.org,
	richard.purdie@linuxfoundation.org

On 4 Aug 2026, at 07:06, Daniel Turull via lists.openembedded.org <daniel.turull=ericsson.com@lists.openembedded.org> wrote:
> The alternative to use a tmp file was to modify _run to allow other encodings than utf-8, but it
> will be a bigger refactor. Or not use _run and call directly the raw subprocess calls.
> 
> I can change it to either option or keep the current patch.

bb.process.run() is meant for use by bitbake itself, it’s a glorified/bad wrapper around subprocess.run() that just does some logging magic whilst also getting in the way and doing things like forcing utf-8...  That magic isn’t useful in devtool, so I think we should be calling subprocess.run() directly.

Ross

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [OE-core] [PATCH v2 2/3] devtool: upgrade: read changelog blobs via a temp file
  2026-08-06 14:05     ` Ross Burton
@ 2026-08-06 14:09       ` Daniel Turull
  0 siblings, 0 replies; 7+ messages in thread
From: Daniel Turull @ 2026-08-06 14:09 UTC (permalink / raw)
  To: Ross.Burton@arm.com
  Cc: openembedded-core@lists.openembedded.org,
	richard.purdie@linuxfoundation.org

On Thu, 2026-08-06 at 14:05 +0000, Ross Burton wrote:
> On 4 Aug 2026, at 07:06, Daniel Turull via lists.openembedded.org
> <daniel.turull=ericsson.com@lists.openembedded.org> wrote:
> > The alternative to use a tmp file was to modify _run to allow other encodings than utf-8, but it
> > will be a bigger refactor. Or not use _run and call directly the raw subprocess calls.
> > 
> > I can change it to either option or keep the current patch.
> 
> bb.process.run() is meant for use by bitbake itself, it’s a glorified/bad wrapper around
> subprocess.run() that just does some logging magic whilst also getting in the way and doing things
> like forcing utf-8...  That magic isn’t useful in devtool, so I think we should be calling
> subprocess.run() directly.
> 
> Ross
ok. I'll change it to that. It was one one of the options that I had in mind but I was not sure if
we wanted to have all going to run()

Thanks Ross.

Daniel

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-08-06 14:09 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-04  6:02 [PATCH v2 0/3] devtool: upgrade: improve changelog extraction for git-log-style ChangeLogs daniel.turull
2026-08-04  6:02 ` [PATCH v2 1/3] devtool: upgrade: ignore changelogs from 3rd party daniel.turull
2026-08-04  6:02 ` [PATCH v2 2/3] devtool: upgrade: read changelog blobs via a temp file daniel.turull
2026-08-04  6:02 ` [PATCH v2 3/3] devtool: upgrade: diff git-log-style changelogs by commit hash daniel.turull
     [not found] ` <18C884DEFF3686EE.1990914@lists.openembedded.org>
2026-08-04  6:06   ` [OE-core] [PATCH v2 2/3] devtool: upgrade: read changelog blobs via a temp file Daniel Turull
2026-08-06 14:05     ` Ross Burton
2026-08-06 14:09       ` Daniel Turull

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox