From ebb5c4b82caec5160544fdbb2d539fd1b5a3abfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= 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 --- 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