From afc9db8bb71bd76dce45f5246f9ea5b52d59a700 Mon Sep 17 00:00:00 2001
From: Gilles Peskine <Gilles.Peskine@arm.com>
Date: Thu, 30 Jan 2020 11:38:01 +0100
Subject: [PATCH] Fix version number recognition heuristics

The regexp was wrong, for example it matched "2.20x" but failed to
match "3.1".

Some test cases:
>>> def f(title):
...  version_number = re.search(_version_number_re, title)
...  if version_number:
...      return not re.search(_incomplete_version_number_re,
...                                  version_number.group(0))
...  else:
...   return False
...
>>> [(s, f(s.encode('ascii'))) for s in ['foo', 'foo 3', 'foo 3.', 'foo 3.1', 'foo 3.14', 'foo 3.2.1', 'foo 3.2.1alpha', 'foo 3.1.a', 'foo 3.a', 'foo 3.x.1']]
[('foo', False), ('foo 3', False), ('foo 3.', False), ('foo 3.1', True), ('foo 3.14', True), ('foo 3.2.1', True), ('foo 3.2.1alpha', True), ('foo 3.1.a', False), ('foo 3.a', False), ('foo 3.x.1', False)]
---
 scripts/assemble_changelog.py | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/scripts/assemble_changelog.py b/scripts/assemble_changelog.py
index 04a5d6c39..a3f720167 100755
--- a/scripts/assemble_changelog.py
+++ b/scripts/assemble_changelog.py
@@ -84,9 +84,10 @@ class ChangeLog:
         return level, line[level:].strip()
 
     # Only accept dotted version numbers (e.g. "3.1", not "3").
-    # Refuse ".x" in a version number: this indicates a version that is
-    # not yet released.
-    _version_number_re = re.compile(br'[0-9]\.[0-9][0-9.]+([^.]|\.[^0-9x])')
+    # Refuse ".x" in a version number where x is a letter: this indicates
+    # a version that is not yet released. Something like "3.1a" is accepted.
+    _version_number_re = re.compile(br'[0-9]+\.[0-9A-Za-z.]+')
+    _incomplete_version_number_re = re.compile(br'.*\.[A-Za-z]')
 
     def section_is_released_version(self, title):
         """Whether this section is for a released version.
@@ -98,7 +99,11 @@ class ChangeLog:
         # that follows a particular pattern. These criteria may be revised
         # as needed in future versions of this script.
         version_number = re.search(self._version_number_re, title)
-        return bool(version_number)
+        if version_number:
+            return not re.search(self._incomplete_version_number_re,
+                                 version_number.group(0))
+        else:
+            return False
 
     def unreleased_version_title(self):
         """The title to use if creating a new section for an unreleased version."""