All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] devtool: upgrade: write changelog metadata as JSON sidecar
@ 2026-09-18 12:01 daniel.turull
  2026-09-18 12:01 ` [PATCH 2/2] devtool: upgrade: clean up changelog content for commit messages daniel.turull
  0 siblings, 1 reply; 2+ messages in thread
From: daniel.turull @ 2026-09-18 12:01 UTC (permalink / raw)
  To: openembedded-core; +Cc: Daniel Turull

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

Add changelogs/<pn>.json alongside the existing changelogs/<pn>.txt,
to include metadata. This makes the upgrade/changelog metadata
machine-readable without having to parse it out of the .txt file.

The .txt file no longer has the 'Changelog for ...'/'Source: ...'
header lines, now containing just the changelog body; that
information moved to the JSON file instead. devtool reset now cleans
up both files.

AI-Generated: Uses Kiro with Claude-sonnet-5
Signed-off-by: Daniel Turull <daniel.turull@ericsson.com>
---
 .../python3-guessing-game_git.bb.changelog    |  2 --
 .../devtool-upgrade-test1_1.5.3.bb.changelog  |  3 ---
 .../devtool-upgrade-test2_git.bb.changelog    |  2 --
 .../devtool-upgrade-test3_1.5.3.bb.changelog  |  3 ---
 .../devtool-upgrade-test4_1.5.3.bb.changelog  |  3 ---
 .../devtool-upgrade-test5_git.bb.changelog    |  2 --
 meta/lib/oeqa/selftest/cases/devtool.py       | 17 +++++++++++++--
 scripts/lib/devtool/standard.py               |  9 +++++---
 scripts/lib/devtool/upgrade.py                | 21 +++++++++++++++----
 9 files changed, 38 insertions(+), 24 deletions(-)

diff --git a/meta-selftest/recipes-devtools/python/python3-guessing-game_git.bb.changelog b/meta-selftest/recipes-devtools/python/python3-guessing-game_git.bb.changelog
index 571220b2a4..ef0052160b 100644
--- a/meta-selftest/recipes-devtools/python/python3-guessing-game_git.bb.changelog
+++ b/meta-selftest/recipes-devtools/python/python3-guessing-game_git.bb.changelog
@@ -1,3 +1 @@
-Changelog for python3-guessing-game: 0.1.0 -> 0.2.0
-
 40cf004 Sync with maturin tutorial source
diff --git a/meta-selftest/recipes-test/devtool/devtool-upgrade-test1_1.5.3.bb.changelog b/meta-selftest/recipes-test/devtool/devtool-upgrade-test1_1.5.3.bb.changelog
index a37f796a0d..15d7baeeb4 100644
--- a/meta-selftest/recipes-test/devtool/devtool-upgrade-test1_1.5.3.bb.changelog
+++ b/meta-selftest/recipes-test/devtool/devtool-upgrade-test1_1.5.3.bb.changelog
@@ -1,6 +1,3 @@
-Changelog for devtool-upgrade-test1: 1.5.3 -> 1.6.0
-Source: doc/NEWS
-
 1.6.0 - 15 March 2015
   - fix lstat64 support when unavailable - separate patches supplied by
     Ganael Laplanche and Peter Korsgaard
diff --git a/meta-selftest/recipes-test/devtool/devtool-upgrade-test2_git.bb.changelog b/meta-selftest/recipes-test/devtool/devtool-upgrade-test2_git.bb.changelog
index b7f0019d56..ac133c483f 100644
--- a/meta-selftest/recipes-test/devtool/devtool-upgrade-test2_git.bb.changelog
+++ b/meta-selftest/recipes-test/devtool/devtool-upgrade-test2_git.bb.changelog
@@ -1,3 +1 @@
-Changelog for devtool-upgrade-test2: 0.1+git -> 0.1+git
-
 6cc6077 dbus-wait.c: Fix typo
diff --git a/meta-selftest/recipes-test/devtool/devtool-upgrade-test3_1.5.3.bb.changelog b/meta-selftest/recipes-test/devtool/devtool-upgrade-test3_1.5.3.bb.changelog
index aaab809736..15d7baeeb4 100644
--- a/meta-selftest/recipes-test/devtool/devtool-upgrade-test3_1.5.3.bb.changelog
+++ b/meta-selftest/recipes-test/devtool/devtool-upgrade-test3_1.5.3.bb.changelog
@@ -1,6 +1,3 @@
-Changelog for devtool-upgrade-test3: 1.5.3 -> 1.6.0
-Source: doc/NEWS
-
 1.6.0 - 15 March 2015
   - fix lstat64 support when unavailable - separate patches supplied by
     Ganael Laplanche and Peter Korsgaard
diff --git a/meta-selftest/recipes-test/devtool/devtool-upgrade-test4_1.5.3.bb.changelog b/meta-selftest/recipes-test/devtool/devtool-upgrade-test4_1.5.3.bb.changelog
index 0ce6547c77..15d7baeeb4 100644
--- a/meta-selftest/recipes-test/devtool/devtool-upgrade-test4_1.5.3.bb.changelog
+++ b/meta-selftest/recipes-test/devtool/devtool-upgrade-test4_1.5.3.bb.changelog
@@ -1,6 +1,3 @@
-Changelog for devtool-upgrade-test4: 1.5.3 -> 1.6.0
-Source: doc/NEWS
-
 1.6.0 - 15 March 2015
   - fix lstat64 support when unavailable - separate patches supplied by
     Ganael Laplanche and Peter Korsgaard
diff --git a/meta-selftest/recipes-test/devtool/devtool-upgrade-test5_git.bb.changelog b/meta-selftest/recipes-test/devtool/devtool-upgrade-test5_git.bb.changelog
index 7c5ecf3505..8077da3e21 100644
--- a/meta-selftest/recipes-test/devtool/devtool-upgrade-test5_git.bb.changelog
+++ b/meta-selftest/recipes-test/devtool/devtool-upgrade-test5_git.bb.changelog
@@ -1,3 +1 @@
-Changelog for devtool-upgrade-test5: 0.1+git -> 0.1+git
-
 0a60d6a Add dummy commit on tip for testing
diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index 84e020cd3d..dda59a3459 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -2337,21 +2337,33 @@ class DevtoolUpgradeTests(DevtoolBase):
         except:
             self.skip("Git user.name and user.email must be set")
 
-    def _check_changelog(self, recipe, oldrecipefile):
+    def _check_changelog(self, recipe, oldrecipefile, old_ver=None, new_ver=None):
         """Compare extracted changelog against reference data."""
         changelog_ref = oldrecipefile + '.changelog'
         self.assertExists(changelog_ref, 'Changelog reference file must exist for %s' % recipe)
         changelog_file = os.path.join(self.workspacedir, 'changelogs', '%s.txt' % recipe)
+        metadata_file = os.path.join(self.workspacedir, 'changelogs', '%s.json' % recipe)
         with open(changelog_ref, 'r') as f:
             expected = f.read()
         if not expected:
             self.assertNotExists(changelog_file,
                 'Changelog file should not exist when reference is empty')
+            self.assertNotExists(metadata_file,
+                'Changelog metadata file should not exist when reference is empty')
         else:
             self.assertExists(changelog_file, 'Changelog file should exist after upgrade')
             with open(changelog_file, 'r') as f:
                 actual = f.read()
             self.assertEqual(expected, actual)
+            self.assertExists(metadata_file, 'Changelog metadata file should exist after upgrade')
+            with open(metadata_file, 'r') as f:
+                metadata = json.load(f)
+            self.assertEqual(metadata.get('package'), recipe)
+            if old_ver:
+                self.assertEqual(metadata.get('old_version'), old_ver)
+            if new_ver:
+                self.assertEqual(metadata.get('new_version'), new_ver)
+            self.assertEqual(metadata.get('changelog_file'), '%s.txt' % recipe)
 
     def test_devtool_upgrade(self):
         # Check preconditions
@@ -2364,6 +2376,7 @@ class DevtoolUpgradeTests(DevtoolBase):
             self.assertIn(param, result.output)
         # For the moment, we are using a real recipe.
         recipe = 'devtool-upgrade-test1'
+        old_version = '1.5.3'
         version = '1.6.0'
         oldrecipefile = get_bb_var('FILE', recipe)
         tempdir = tempfile.mkdtemp(prefix='devtoolqa')
@@ -2392,7 +2405,7 @@ class DevtoolUpgradeTests(DevtoolBase):
             newlines = f.readlines()
         self.assertEqual(desiredlines, newlines)
         # Check changelog
-        self._check_changelog(recipe, oldrecipefile)
+        self._check_changelog(recipe, oldrecipefile, old_version, version)
         # Check devtool reset recipe
         result = runCmd('devtool reset %s -n' % recipe)
         result = runCmd('devtool status')
diff --git a/scripts/lib/devtool/standard.py b/scripts/lib/devtool/standard.py
index ac9e7cd61a..0d933e68b1 100644
--- a/scripts/lib/devtool/standard.py
+++ b/scripts/lib/devtool/standard.py
@@ -2074,11 +2074,14 @@ def _reset(recipes, no_clean, remove_work, config, basepath, workspace):
 
         # Clean up changelog if present
         changelog_file = os.path.join(config.workspace_path, 'changelogs', '%s.txt' % pn)
+        metadata_file = os.path.join(config.workspace_path, 'changelogs', '%s.json' % pn)
         if os.path.exists(changelog_file):
             os.remove(changelog_file)
-            changelog_dir = os.path.dirname(changelog_file)
-            if not os.listdir(changelog_dir):
-                os.rmdir(changelog_dir)
+        if os.path.exists(metadata_file):
+            os.remove(metadata_file)
+        changelog_dir = os.path.join(config.workspace_path, 'changelogs')
+        if os.path.exists(changelog_dir) and not os.listdir(changelog_dir):
+            os.rmdir(changelog_dir)
 
 def reset(args, config, basepath, workspace):
     """Entry point for the devtool 'reset' subcommand"""
diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
index 58165541f6..cdc85fe7ae 100644
--- a/scripts/lib/devtool/upgrade.py
+++ b/scripts/lib/devtool/upgrade.py
@@ -9,6 +9,7 @@
 import os
 import sys
 import re
+import json
 import shlex
 import shutil
 import subprocess
@@ -730,13 +731,23 @@ def _extract_changelog(srctree, pn, old_ver, new_ver, old_tag, new_tag, workspac
     bb.utils.mkdirhier(changelog_dir)
     changelog_path = os.path.join(changelog_dir, '%s.txt' % pn)
     with open(changelog_path, 'w') as f:
-        f.write('Changelog for %s: %s -> %s\n' % (pn, old_ver, new_ver))
-        if changelog_fname:
-            f.write('Source: %s\n' % changelog_fname)
-        f.write('\n')
         f.write(changelog_content)
         f.write('\n')
 
+    # Metadata sidecar file: keeps upgrade/changelog details machine-readable
+    # while the changelog text itself stays in the .txt file.
+    metadata = {
+        'package': pn,
+        'old_version': old_ver,
+        'new_version': new_ver,
+        'changelog_file': os.path.basename(changelog_path),
+        'changelog_source': changelog_fname.split(', ') if changelog_fname else None,
+    }
+    metadata_path = os.path.join(changelog_dir, '%s.json' % pn)
+    with open(metadata_path, 'w') as f:
+        json.dump(metadata, f, indent=4)
+        f.write('\n')
+
     return changelog_path
 
 def upgrade(args, config, basepath, workspace):
@@ -831,6 +842,8 @@ def upgrade(args, config, basepath, workspace):
                                             config.workspace_path, is_git)
         if changelog_file:
             logger.info('Changelog extracted to %s' % changelog_file)
+            metadata_file = os.path.join(os.path.dirname(changelog_file), '%s.json' % pn)
+            logger.info('Changelog metadata written to %s' % metadata_file)
 
         if license_diff:
             logger.info('License checksums have been updated in the new recipe; please refer to it for the difference between the old and the new license texts.')


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

* [PATCH 2/2] devtool: upgrade: clean up changelog content for commit messages
  2026-09-18 12:01 [PATCH 1/2] devtool: upgrade: write changelog metadata as JSON sidecar daniel.turull
@ 2026-09-18 12:01 ` daniel.turull
  0 siblings, 0 replies; 2+ messages in thread
From: daniel.turull @ 2026-09-18 12:01 UTC (permalink / raw)
  To: openembedded-core; +Cc: Daniel Turull

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

Strip noise that has no value in a commit message:
- GitHub PR references: trailing "(#NNN)" and full pull/issue URLs.
- Commit hash prefixes from "git log --oneline"-style content,
  including the git-log fallback used when no changelog file exists.
- GNU ChangeLog noise: "YYYY-MM-DD  Name  <email>" entry headers, and
  "* file (func):" / "+ commit <hash>" bullets plus their wrapped
  continuation lines, down to the next blank line, keeping only the
  human-readable summary of each entry. Remaining tabs are stripped.
- NEWS-style "Changes in X.Y.Z, DATE" release headers and their "==="
  underline (libportal/glib-family projects).

Lines exceeding 80 chars are wrapped, preserving existing indentation.

AI-Generated: Uses Kiro with Claude Sonnet 5
Signed-off-by: Daniel Turull <daniel.turull@ericsson.com>
---
 .../python3-guessing-game_git.bb.changelog    |  2 +-
 .../devtool-upgrade-test2_git.bb.changelog    |  2 +-
 .../devtool-upgrade-test5_git.bb.changelog    |  2 +-
 scripts/lib/devtool/upgrade.py                | 63 ++++++++++++++++++-
 4 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/meta-selftest/recipes-devtools/python/python3-guessing-game_git.bb.changelog b/meta-selftest/recipes-devtools/python/python3-guessing-game_git.bb.changelog
index ef0052160b..6c7bf9f713 100644
--- a/meta-selftest/recipes-devtools/python/python3-guessing-game_git.bb.changelog
+++ b/meta-selftest/recipes-devtools/python/python3-guessing-game_git.bb.changelog
@@ -1 +1 @@
-40cf004 Sync with maturin tutorial source
+Sync with maturin tutorial source
diff --git a/meta-selftest/recipes-test/devtool/devtool-upgrade-test2_git.bb.changelog b/meta-selftest/recipes-test/devtool/devtool-upgrade-test2_git.bb.changelog
index ac133c483f..4699b1e89e 100644
--- a/meta-selftest/recipes-test/devtool/devtool-upgrade-test2_git.bb.changelog
+++ b/meta-selftest/recipes-test/devtool/devtool-upgrade-test2_git.bb.changelog
@@ -1 +1 @@
-6cc6077 dbus-wait.c: Fix typo
+dbus-wait.c: Fix typo
diff --git a/meta-selftest/recipes-test/devtool/devtool-upgrade-test5_git.bb.changelog b/meta-selftest/recipes-test/devtool/devtool-upgrade-test5_git.bb.changelog
index 8077da3e21..3196cc13bd 100644
--- a/meta-selftest/recipes-test/devtool/devtool-upgrade-test5_git.bb.changelog
+++ b/meta-selftest/recipes-test/devtool/devtool-upgrade-test5_git.bb.changelog
@@ -1 +1 @@
-0a60d6a Add dummy commit on tip for testing
+Add dummy commit on tip for testing
diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
index cdc85fe7ae..ba7348664f 100644
--- a/scripts/lib/devtool/upgrade.py
+++ b/scripts/lib/devtool/upgrade.py
@@ -14,6 +14,7 @@ import shlex
 import shutil
 import subprocess
 import tempfile
+import textwrap
 import logging
 import argparse
 import scriptutils
@@ -634,6 +635,66 @@ def _diff_git_log_changelog(old_content, new_content):
     return '\n'.join(subjects) if subjects else None
 
 
+# GitHub PR references: trailing (#123) at end of line, or full URLs
+_GITHUB_PR_RE = re.compile(r'\s*\(#[0-9]+\)\s*$|\s*https?://github\.com/[^/]+/[^/]+/(pull|issues)/[0-9]+\s*')
+# Commit hash prefixes from git log --oneline: "abc1234 "
+_COMMIT_HASH_RE = re.compile(r'^[0-9a-f]{7,40}\s+')
+# A GNU ChangeLog entry: starts at either an entry header
+# ("2026-08-24  Werner Koch  <wk@gnupg.org>") or a technical bullet
+# ("* file.c (func): text" or "+ commit <hash>"), and extends through all
+# wrapped continuation lines up to (but not including) the next blank line.
+_GNU_CHANGELOG_ENTRY_RE = re.compile(
+    r'^(?:\d{4}-\d{2}-\d{2}\s+.+<.+@.+>|\s*(?:\*\s|\+ commit [0-9a-f]{7,40}).*)'
+    r'(?:\n(?!\s*$).*)*\n?',
+    re.MULTILINE)
+# NEWS-style release header + underline (glib/gtk family):
+# "Changes in 0.11.0, 2026-09-12\n===============================\n"
+_NEWS_RELEASE_HEADER_RE = re.compile(r'^Changes in .+,\s*\d{4}-\d{2}-\d{2}\s*\n=+\s*$', re.MULTILINE)
+
+def _join_single_line_paragraphs(text):
+    """Drop the blank line between two adjacent one-line paragraphs (each
+    surrounded by blank lines), so lines left behind by GNU ChangeLog entry
+    stripping read as one entry per line instead of double-spaced."""
+    paragraphs = text.split('\n\n')
+    out = [paragraphs[0]]
+    prev_is_single_line = '\n' not in paragraphs[0]
+    for para in paragraphs[1:]:
+        this_is_single_line = '\n' not in para
+        if prev_is_single_line and this_is_single_line:
+            out[-1] += '\n' + para
+        else:
+            out.append(para)
+        prev_is_single_line = this_is_single_line
+    return '\n\n'.join(out)
+
+
+def _cleanup_changelog(content):
+    """Strip GitHub PR refs, commit hashes, GNU ChangeLog entries (headers
+    and file/function bullets, plus their wrapped continuation lines) and
+    NEWS-style 'Changes in X.Y.Z, DATE' release headers with their
+    underline. Wrap lines over 80 chars, preserving indentation."""
+    content = _NEWS_RELEASE_HEADER_RE.sub('', content)
+    content = _GNU_CHANGELOG_ENTRY_RE.sub('', content)
+    out = []
+    for line in content.splitlines():
+        line = _GITHUB_PR_RE.sub(' ', line)
+        line = _COMMIT_HASH_RE.sub('', line)
+        line = line.replace('\t', '').rstrip()
+        stripped = line.lstrip()
+        indent = line[:len(line) - len(stripped)]
+        if len(line) > 80:
+            if stripped.startswith('- '):
+                indent += '  '
+            line = textwrap.fill(line, width=80, subsequent_indent=indent)
+        out.append(line)
+    text = '\n'.join(out)
+    # Entries removed above can leave 3+ blank newlines where a blank-line
+    # separator butted up against a removed entry's own blank line;
+    # collapse any such run down to a single blank line (2 newlines).
+    text = re.sub(r'\n{3,}', '\n\n', text).strip()
+    return _join_single_line_paragraphs(text)
+
+
 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
@@ -723,7 +784,7 @@ def _extract_changelog(srctree, pn, old_ver, new_ver, old_tag, new_tag, workspac
         changelog_content = ''.join(filtered)
 
     # Clean up content for readability and commit message use
-    changelog_content = re.sub(r'\n{3,}', '\n\n', changelog_content).strip()
+    changelog_content = _cleanup_changelog(changelog_content)
     if not changelog_content:
         return None
 


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

end of thread, other threads:[~2026-09-18 12:02 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-18 12:01 [PATCH 1/2] devtool: upgrade: write changelog metadata as JSON sidecar daniel.turull
2026-09-18 12:01 ` [PATCH 2/2] devtool: upgrade: clean up changelog content for commit messages daniel.turull

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.