Blob Blame History Raw
From ebb5c4b82caec5160544fdbb2d539fd1b5a3abfb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= <miro@hroncok.cz>
Date: Thu, 5 Oct 2023 16:59:22 +0200
Subject: [PATCH 2/5] *pkg import: Undo rpmautospec processing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fixes https://pagure.io/fedpkg/issue/527

Depends-on: https://pagure.io/fedora-infra/rpmautospec/pull-request/312

Signed-off-by: Miro HronĨok <miro@hroncok.cz>
---
 pyrpkg/__init__.py | 22 ++++++-------
 pyrpkg/utils.py    | 77 ++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 78 insertions(+), 21 deletions(-)

diff --git a/pyrpkg/__init__.py b/pyrpkg/__init__.py
index b257fec..f562e71 100644
--- a/pyrpkg/__init__.py
+++ b/pyrpkg/__init__.py
@@ -53,7 +53,7 @@ from pyrpkg.sources import SourcesFile
 from pyrpkg.spec import SpecFile
 from pyrpkg.utils import (cached_property, extract_srpm, find_me,
                           is_file_tracked, is_lookaside_eligible_file,
-                          log_result, spec_file_processed_by_rpmautospec)
+                          log_result, spec_file_undo_rpmautospec)
 
 from .gitignore import GitIgnore
 
@@ -1473,16 +1473,6 @@ class Commands(object):
                     uploadfiles.append(file)
                 else:
                     files.append(file)
-
-                # Check all specfiles in SRPM. At this point (the 'import' command can run under
-                # the dist-git repo without any specfiles - right after initialization) we are
-                # not able determine which the main specfile is.
-                if file.endswith('.spec') and not file.startswith('.') \
-                        and not self.allow_pre_generated_srpm \
-                        and spec_file_processed_by_rpmautospec(file, target_dir):
-                    raise rpkgError('SRPM was processed by rpmautospec '
-                                    '(specfile "{}" was analyzed)'.format(file))
-
         finally:
             shutil.rmtree(target_dir)
 
@@ -2081,6 +2071,16 @@ class Commands(object):
             os.chdir(oldpath)
             raise rpkgError("Got an error from rpm2cpio: %s" % err)
 
+        # Undo rpmautospec from all the spec files.
+        # At this point (the 'import' command can run under the dist-git repo
+        # without any specfiles - right after initialization) we are
+        # not able determine which the main specfile is.
+        if not self.allow_pre_generated_srpm:
+            for file in files:
+                if file.endswith('.spec') and not file.startswith('.'):
+                    if spec_file_undo_rpmautospec(file):
+                        self.log.debug("rpmautospec processing removed from {0}".format(file))
+
         # And finally add all the files we know about (and our stock files)
         for file in ('.gitignore', 'sources'):
             if not os.path.exists(file):
diff --git a/pyrpkg/utils.py b/pyrpkg/utils.py
index 3337bdb..f3c8b25 100644
--- a/pyrpkg/utils.py
+++ b/pyrpkg/utils.py
@@ -336,22 +336,79 @@ def is_lookaside_eligible_file(file_name, dir_path=None):
     return encoding == "binary"
 
 
-def spec_file_processed_by_rpmautospec(file_name, dir_path=None):
+def _replace_lines(lines, startline, endline, replacement_lines=None, strip_endline=False):
+    replacement_lines = replacement_lines or []
+    try:
+        start = lines.index(startline)
+        end = lines.index(endline, start)
+    except ValueError:
+        # if both are missing, nothing to do, all good
+        # if only one of them is present, we better not touch it
+        return lines, False
+    else:
+        # rpmautospec adds an empty line after the end
+        # we want to remove it, but only if it is actually empty
+        if strip_endline and lines[end+1] == "\n":
+            end += 1
+        lines = lines[:start] + replacement_lines + lines[end+1:]
+        return lines, True
+
+
+def spec_file_undo_rpmautospec(file_name, dir_path=None):
+    """
+    Given a path to specfile, undo changes generated by rpmautospec.
+    Iff there is something to undo, the specfile will be overwritten.
+
+    Namely:
+
+     1. Removes everything between the following lines:
+          ## START: Set by rpmautospec
+          ## END: Set by rpmautospec
+     2. Replaces everything between the following lines with %autochangelog:
+          ## START: Generated by rpmautospec
+          ## END: Generated by rpmautospec
+
+    Both of the steps only happen once. If the specfile contains multiple such sections,
+    only the first one is removed/replaced.
+
+    The saved spec file is not guaranteed to be bit-by-bit identical with the original
+    spec file used as an input to rpmautospec.
+    However, subsequent repeated conversions there and back should be quite stable.
+
+    The return value says whether the specfile was overwritten.
+    """
     file_path = os.path.join(dir_path or "", file_name)
 
     try:
-        contents = open(file_path).readlines()
+        with open(file_path) as f:
+            contents = f.readlines()
     except Exception:
-        # if we can't read it, let's assume the answer is "no".
+        # if we can't read it, let's do nothing
         return False
 
-    # Check for the %autorelease header prepended to the file
-    if any('START: Set by rpmautospec' in line for line in contents[:10]):
+    # remove the generated macro section near the beginning of the specfile
+    contents, was_removed = _replace_lines(
+        contents,
+        '## START: Set by rpmautospec\n',
+        '## END: Set by rpmautospec\n',
+        strip_endline=True)
+
+    # replace the generated changelog with %autochangelog
+    # note that this does not generally produce content identical to the original
+    # e.g. the macro could have been conditionalized or in curly brackets
+    # most importantly, the %changelog section might have been omitted entirely
+    # however, this should be Good Enough for most of us
+    contents, was_replaced = _replace_lines(
+        contents,
+        '## START: Generated by rpmautospec\n',
+        '## END: Generated by rpmautospec\n',
+        ['%autochangelog\n'])
+
+    # finally, replace the spec if needed
+    # if we cannot write it, better blow up
+    if was_removed or was_replaced:
+        with open(file_path, 'w') as f:
+            f.writelines(contents)
         return True
 
-    # It seems that currently there's no mechanism to detect
-    # %autochangelog processing. But most packages would use both
-    # %autochangelog and %autorelease together, so we should catch
-    # most cases by checking for %autorelease only.
-    # https://pagure.io/fedora-infra/rpmautospec/issue/269
     return False
-- 
2.41.0