prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  [RPM M1] Add a new block to call the generation code for RPM
  Tasks | Estimate | Status | Notes | Dependencies
  -- | -- | -- | -- | --
  The generation code should pull the artifacts from the build workflow to a temporary location | 1 | Complete |   | Build workflow must provide usable artifacts
  The code will call existing install function to install plugins on min artifacts | 1 | Complete |   |  
  After installation, the code will execute a tool or utility to wrap all the content into a RPM package | 5 | Complete | Require writing a script to utilize FPM to start with and later implement in pure python code. <br><br>20220204: We might change to rpmbuild directly without using FPM. See comments. | FPM usages
  The code will also add dependencies to the RPM package so that things like JDK and additional libs for plugins can be installed and pulled separately | 5 | Complete | Need to study on RPM dependency setups | RPM Build Dependencies and the locations of each dependent artifact
  The code will move the RPM package from the temp location to dist folder | 2 | Complete |   |  
  
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/assemble_workflow/bundle_rpm.py`
  Content:
  ```
  1 # SPDX-License-Identifier: Apache-2.0
  2 #
  3 # The OpenSearch Contributors require contributions made to
  4 # this file be licensed under the Apache-2.0 license or a
  5 # compatible open source license.
  6 
  7 import logging
  8 import os
  9 import shutil
  10 import subprocess
  11 
  12 from manifests.build_manifest import BuildManifest
  13 from system.os import rpm_architecture
  14 
  15 
  16 class BundleRpm:
  17 
  18     def __init__(self, filename: str, package_path: str, min_path: str) -> None:
  19         self.filename = filename
  20         self.package_path = package_path
  21         self.min_path = min_path
  22 
  23     def extract(self, dest: str) -> None:
  24         cpio_basename = os.path.splitext(os.path.basename(self.package_path))[0]
  25         cpio_path = os.path.join(dest, f"{cpio_basename}.cpio")
  26         min_source_path = os.path.join(dest, 'usr', 'share', self.filename)
  27         min_dest_path = os.path.join(dest, self.min_path)
  28         min_config_path = os.path.join(dest, 'etc', self.filename)
  29         min_bin_env_path = os.path.join(min_dest_path, 'bin', f"{self.filename}-env")
  30 
  31         # Convert rpm to cpio so we can extract the content
  32         logging.info(f"Convert rpm to cpio for extraction: {self.package_path} to {cpio_path}")
  33         with open(cpio_path, 'wb') as fp:
  34             subprocess.check_call(
  35                 [
  36                     'rpm2cpio',
  37                     self.package_path,
  38                 ],
  39                 stdout=fp,
  40                 cwd=dest,
  41             )
  42 
  43         # Extract cpio archive based on the rpm package
  44         logging.info(f"Extract cpio {cpio_path} content to {dest}")
  45         with open(cpio_path, 'rb') as fp:
  46             subprocess.check_call(
  47                 [
  48                     'cpio',
  49                     '-imdv',
  50                 ],
  51                 stdin=fp,
  52                 stdout=subprocess.DEVNULL,
  53                 stderr=subprocess.STDOUT,
  54                 cwd=dest,
  55             )
  56 
  57         # Move core folder destination so plugin install can proceed
  58         logging.info(f"Move {min_source_path} to {min_dest_path} for plugin installation")
  59         shutil.move(min_source_path, min_dest_path)
  60 
  61         # Multiple modifications and env vars setups before install plugins
  62         # As bin/opensearch-env is different between archive and package
  63         # https://github.com/opensearch-project/OpenSearch/issues/2092
  64         os.environ[f"{self.filename.upper()}_PATH_CONF"] = min_config_path
  65 
  66         if os.path.exists(min_bin_env_path):
  67             # Backup original file
  68             shutil.copy2(min_bin_env_path, f"{min_bin_env_path}.backup")
  69             # Prevent sourcing as file is only in place after rpm installation
  70             # So that min can install plugin zips
  71             # Have to decode then encode back to ascii due to mypy complains TextIO not equals to BinaryIO
  72             with open(min_bin_env_path, 'rb') as fp:
  73                 min_bin_env_lines = fp.read().decode('ascii')
  74 
  75             with open(min_bin_env_path, 'wb') as fp:
  76                 fp.write(min_bin_env_lines.replace('source', '#source').encode('ascii'))
  77 
  78     def build(self, name: str, dest: str, archive_path: str, build_cls: BuildManifest.Build) -> None:
  79         # extract dest and build dest are not the same, this is restoring the extract dest
  80         # mainly due to rpm requires several different setups compares to tarball and zip
  81         ext_dest = os.path.dirname(archive_path)
  82         min_source_path = os.path.join(ext_dest, 'usr', 'share', self.filename)
  83         min_dest_path = os.path.join(ext_dest, self.min_path)
  84         min_bin_env_path = os.path.join(min_dest_path, 'bin', f"{self.filename}-env")
  85         bundle_artifact_path: str = None
  86 
  87         # Remove env var
  88         logging.info('Organize folder structure before generating rpm')
  89         os.environ.pop('OPENSEARCH_PATH_CONF', None)
  90 
  91         # Restore config file and core folder to original location
  92         shutil.move(f"{min_bin_env_path}.backup", min_bin_env_path)
  93         shutil.move(min_dest_path, min_source_path)
  94 
  95         # Run bundle rpmbuild
  96         bundle_cmd = " ".join(
  97             [
  98                 'rpmbuild',
  99                 '-bb',
  100                 f"--define '_topdir {ext_dest}'",
  101                 f"--define '_version {build_cls.version}'",
  102                 f"--define '_architecture {rpm_architecture(build_cls.architecture)}'",
  103                 f"{self.filename}.rpm.spec",
  104             ]
  105         )
  106 
  107         logging.info(f"Execute {bundle_cmd} in {ext_dest}")
  108         subprocess.check_call(bundle_cmd, cwd=ext_dest, shell=True)
  109 
  110         # Move artifact to repo root before being published to {dest}
  111         for dirpath, dirnames, filenames in os.walk(os.path.join(ext_dest, 'RPMS')):
  112             for filename in [file for file in filenames if file.endswith('.rpm')]:
  113                 bundle_artifact_path = os.path.join(dirpath, filename)
  114                 break
  115 
  116         shutil.move(bundle_artifact_path, name)
  117
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/src/assemble_workflow/bundle_rpm.py b/src/assemble_workflow/bundle_rpm.py
  --- a/src/assemble_workflow/bundle_rpm.py
  +++ b/src/assemble_workflow/bundle_rpm.py
  @@ -89,7 +89,10 @@
           os.environ.pop('OPENSEARCH_PATH_CONF', None)
   
           # Restore config file and core folder to original location
  -        shutil.move(f"{min_bin_env_path}.backup", min_bin_env_path)
  +        if os.path.exists(f"{min_bin_env_path}.backup"):
  +            logging.info(f"Restore {min_bin_env_path}.backup to {min_bin_env_path}")
  +            shutil.move(f"{min_bin_env_path}.backup", min_bin_env_path)
  +
           shutil.move(min_dest_path, min_source_path)
   
           # Run bundle rpmbuild
  
verification_info: {"golden_diff": "diff --git a/src/assemble_workflow/bundle_rpm.py b/src/assemble_workflow/bundle_rpm.py\n--- a/src/assemble_workflow/bundle_rpm.py\n+++ b/src/assemble_workflow/bundle_rpm.py\n@@ -89,7 +89,10 @@\n         os.environ.pop('OPENSEARCH_PATH_CONF', None)\n \n         # Restore config file and core folder to original location\n-        shutil.move(f\"{min_bin_env_path}.backup\", min_bin_env_path)\n+        if os.path.exists(f\"{min_bin_env_path}.backup\"):\n+            logging.info(f\"Restore {min_bin_env_path}.backup to {min_bin_env_path}\")\n+            shutil.move(f\"{min_bin_env_path}.backup\", min_bin_env_path)\n+\n         shutil.move(min_dest_path, min_source_path)\n \n         # Run bundle rpmbuild\n", "issue": "[RPM M1] Add a new block to call the generation code for RPM\nTasks | Estimate | Status | Notes | Dependencies\r\n-- | -- | -- | -- | --\r\nThe generation code should pull the artifacts from the build workflow to a temporary location | 1 | Complete | \u00a0 | Build workflow must provide usable artifacts\r\nThe code will call existing install function to install plugins on min artifacts | 1 | Complete | \u00a0 | \u00a0\r\nAfter installation, the code will execute a tool or utility to wrap all the content into a RPM package | 5 | Complete | Require writing a script to utilize FPM to start with and later implement in pure python code. <br><br>20220204: We might change to rpmbuild directly without using FPM. See comments. | FPM usages\r\nThe code will also add dependencies to the RPM package so that things like JDK and additional libs for plugins can be installed and pulled separately | 5 | Complete | Need to study on RPM dependency setups | RPM Build Dependencies and the locations of each dependent artifact\r\nThe code will move the RPM package from the temp location to dist folder | 2 | Complete | \u00a0 | \u00a0\r\n\r\n\n", "before_files": [{"content": "# SPDX-License-Identifier: Apache-2.0\n#\n# The OpenSearch Contributors require contributions made to\n# this file be licensed under the Apache-2.0 license or a\n# compatible open source license.\n\nimport logging\nimport os\nimport shutil\nimport subprocess\n\nfrom manifests.build_manifest import BuildManifest\nfrom system.os import rpm_architecture\n\n\nclass BundleRpm:\n\n    def __init__(self, filename: str, package_path: str, min_path: str) -> None:\n        self.filename = filename\n        self.package_path = package_path\n        self.min_path = min_path\n\n    def extract(self, dest: str) -> None:\n        cpio_basename = os.path.splitext(os.path.basename(self.package_path))[0]\n        cpio_path = os.path.join(dest, f\"{cpio_basename}.cpio\")\n        min_source_path = os.path.join(dest, 'usr', 'share', self.filename)\n        min_dest_path = os.path.join(dest, self.min_path)\n        min_config_path = os.path.join(dest, 'etc', self.filename)\n        min_bin_env_path = os.path.join(min_dest_path, 'bin', f\"{self.filename}-env\")\n\n        # Convert rpm to cpio so we can extract the content\n        logging.info(f\"Convert rpm to cpio for extraction: {self.package_path} to {cpio_path}\")\n        with open(cpio_path, 'wb') as fp:\n            subprocess.check_call(\n                [\n                    'rpm2cpio',\n                    self.package_path,\n                ],\n                stdout=fp,\n                cwd=dest,\n            )\n\n        # Extract cpio archive based on the rpm package\n        logging.info(f\"Extract cpio {cpio_path} content to {dest}\")\n        with open(cpio_path, 'rb') as fp:\n            subprocess.check_call(\n                [\n                    'cpio',\n                    '-imdv',\n                ],\n                stdin=fp,\n                stdout=subprocess.DEVNULL,\n                stderr=subprocess.STDOUT,\n                cwd=dest,\n            )\n\n        # Move core folder destination so plugin install can proceed\n        logging.info(f\"Move {min_source_path} to {min_dest_path} for plugin installation\")\n        shutil.move(min_source_path, min_dest_path)\n\n        # Multiple modifications and env vars setups before install plugins\n        # As bin/opensearch-env is different between archive and package\n        # https://github.com/opensearch-project/OpenSearch/issues/2092\n        os.environ[f\"{self.filename.upper()}_PATH_CONF\"] = min_config_path\n\n        if os.path.exists(min_bin_env_path):\n            # Backup original file\n            shutil.copy2(min_bin_env_path, f\"{min_bin_env_path}.backup\")\n            # Prevent sourcing as file is only in place after rpm installation\n            # So that min can install plugin zips\n            # Have to decode then encode back to ascii due to mypy complains TextIO not equals to BinaryIO\n            with open(min_bin_env_path, 'rb') as fp:\n                min_bin_env_lines = fp.read().decode('ascii')\n\n            with open(min_bin_env_path, 'wb') as fp:\n                fp.write(min_bin_env_lines.replace('source', '#source').encode('ascii'))\n\n    def build(self, name: str, dest: str, archive_path: str, build_cls: BuildManifest.Build) -> None:\n        # extract dest and build dest are not the same, this is restoring the extract dest\n        # mainly due to rpm requires several different setups compares to tarball and zip\n        ext_dest = os.path.dirname(archive_path)\n        min_source_path = os.path.join(ext_dest, 'usr', 'share', self.filename)\n        min_dest_path = os.path.join(ext_dest, self.min_path)\n        min_bin_env_path = os.path.join(min_dest_path, 'bin', f\"{self.filename}-env\")\n        bundle_artifact_path: str = None\n\n        # Remove env var\n        logging.info('Organize folder structure before generating rpm')\n        os.environ.pop('OPENSEARCH_PATH_CONF', None)\n\n        # Restore config file and core folder to original location\n        shutil.move(f\"{min_bin_env_path}.backup\", min_bin_env_path)\n        shutil.move(min_dest_path, min_source_path)\n\n        # Run bundle rpmbuild\n        bundle_cmd = \" \".join(\n            [\n                'rpmbuild',\n                '-bb',\n                f\"--define '_topdir {ext_dest}'\",\n                f\"--define '_version {build_cls.version}'\",\n                f\"--define '_architecture {rpm_architecture(build_cls.architecture)}'\",\n                f\"{self.filename}.rpm.spec\",\n            ]\n        )\n\n        logging.info(f\"Execute {bundle_cmd} in {ext_dest}\")\n        subprocess.check_call(bundle_cmd, cwd=ext_dest, shell=True)\n\n        # Move artifact to repo root before being published to {dest}\n        for dirpath, dirnames, filenames in os.walk(os.path.join(ext_dest, 'RPMS')):\n            for filename in [file for file in filenames if file.endswith('.rpm')]:\n                bundle_artifact_path = os.path.join(dirpath, filename)\n                break\n\n        shutil.move(bundle_artifact_path, name)\n", "path": "src/assemble_workflow/bundle_rpm.py"}], "after_files": [{"content": "# SPDX-License-Identifier: Apache-2.0\n#\n# The OpenSearch Contributors require contributions made to\n# this file be licensed under the Apache-2.0 license or a\n# compatible open source license.\n\nimport logging\nimport os\nimport shutil\nimport subprocess\n\nfrom manifests.build_manifest import BuildManifest\nfrom system.os import rpm_architecture\n\n\nclass BundleRpm:\n\n    def __init__(self, filename: str, package_path: str, min_path: str) -> None:\n        self.filename = filename\n        self.package_path = package_path\n        self.min_path = min_path\n\n    def extract(self, dest: str) -> None:\n        cpio_basename = os.path.splitext(os.path.basename(self.package_path))[0]\n        cpio_path = os.path.join(dest, f\"{cpio_basename}.cpio\")\n        min_source_path = os.path.join(dest, 'usr', 'share', self.filename)\n        min_dest_path = os.path.join(dest, self.min_path)\n        min_config_path = os.path.join(dest, 'etc', self.filename)\n        min_bin_env_path = os.path.join(min_dest_path, 'bin', f\"{self.filename}-env\")\n\n        # Convert rpm to cpio so we can extract the content\n        logging.info(f\"Convert rpm to cpio for extraction: {self.package_path} to {cpio_path}\")\n        with open(cpio_path, 'wb') as fp:\n            subprocess.check_call(\n                [\n                    'rpm2cpio',\n                    self.package_path,\n                ],\n                stdout=fp,\n                cwd=dest,\n            )\n\n        # Extract cpio archive based on the rpm package\n        logging.info(f\"Extract cpio {cpio_path} content to {dest}\")\n        with open(cpio_path, 'rb') as fp:\n            subprocess.check_call(\n                [\n                    'cpio',\n                    '-imdv',\n                ],\n                stdin=fp,\n                stdout=subprocess.DEVNULL,\n                stderr=subprocess.STDOUT,\n                cwd=dest,\n            )\n\n        # Move core folder destination so plugin install can proceed\n        logging.info(f\"Move {min_source_path} to {min_dest_path} for plugin installation\")\n        shutil.move(min_source_path, min_dest_path)\n\n        # Multiple modifications and env vars setups before install plugins\n        # As bin/opensearch-env is different between archive and package\n        # https://github.com/opensearch-project/OpenSearch/issues/2092\n        os.environ[f\"{self.filename.upper()}_PATH_CONF\"] = min_config_path\n\n        if os.path.exists(min_bin_env_path):\n            # Backup original file\n            shutil.copy2(min_bin_env_path, f\"{min_bin_env_path}.backup\")\n            # Prevent sourcing as file is only in place after rpm installation\n            # So that min can install plugin zips\n            # Have to decode then encode back to ascii due to mypy complains TextIO not equals to BinaryIO\n            with open(min_bin_env_path, 'rb') as fp:\n                min_bin_env_lines = fp.read().decode('ascii')\n\n            with open(min_bin_env_path, 'wb') as fp:\n                fp.write(min_bin_env_lines.replace('source', '#source').encode('ascii'))\n\n    def build(self, name: str, dest: str, archive_path: str, build_cls: BuildManifest.Build) -> None:\n        # extract dest and build dest are not the same, this is restoring the extract dest\n        # mainly due to rpm requires several different setups compares to tarball and zip\n        ext_dest = os.path.dirname(archive_path)\n        min_source_path = os.path.join(ext_dest, 'usr', 'share', self.filename)\n        min_dest_path = os.path.join(ext_dest, self.min_path)\n        min_bin_env_path = os.path.join(min_dest_path, 'bin', f\"{self.filename}-env\")\n        bundle_artifact_path: str = None\n\n        # Remove env var\n        logging.info('Organize folder structure before generating rpm')\n        os.environ.pop('OPENSEARCH_PATH_CONF', None)\n\n        # Restore config file and core folder to original location\n        if os.path.exists(f\"{min_bin_env_path}.backup\"):\n            logging.info(f\"Restore {min_bin_env_path}.backup to {min_bin_env_path}\")\n            shutil.move(f\"{min_bin_env_path}.backup\", min_bin_env_path)\n\n        shutil.move(min_dest_path, min_source_path)\n\n        # Run bundle rpmbuild\n        bundle_cmd = \" \".join(\n            [\n                'rpmbuild',\n                '-bb',\n                f\"--define '_topdir {ext_dest}'\",\n                f\"--define '_version {build_cls.version}'\",\n                f\"--define '_architecture {rpm_architecture(build_cls.architecture)}'\",\n                f\"{self.filename}.rpm.spec\",\n            ]\n        )\n\n        logging.info(f\"Execute {bundle_cmd} in {ext_dest}\")\n        subprocess.check_call(bundle_cmd, cwd=ext_dest, shell=True)\n\n        # Move artifact to repo root before being published to {dest}\n        for dirpath, dirnames, filenames in os.walk(os.path.join(ext_dest, 'RPMS')):\n            for filename in [file for file in filenames if file.endswith('.rpm')]:\n                bundle_artifact_path = os.path.join(dirpath, filename)\n                break\n\n        shutil.move(bundle_artifact_path, name)\n", "path": "src/assemble_workflow/bundle_rpm.py"}]}