We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[WIN M1] WIN (ZIP/MSI) Build/Assemble Process 
|Tasks(ZIP) |**Tasks(MSI)** |Estimate   |Status(ZIP)    |Status(MSI)    |Notes  |
|---    |---    |---    |---    |---    |---    |
|Re-use the existing build process to generate the  OpenSearch/Dashboards min + all of the plugins artifacts for WIN package to  use |Same   |0  |Completed  |Completed  |   |
|The artifacts should be built with ~~LINUX~~ Windows platform  specified, ~~as we will cross-compile WIN binary on LINUX then package with  WINDOWS JDK~~.  |Same   |1  |Completed    |Completed   |~~This is still in debate as we can techinically build WIN  on Windows machine, but there are a lot of things to setup just so Jenkins  can run Python on Windows Agent.~~ We are able to run shell scripts natively on Windows agent  |
|We already have "zip" supported for "—distribution“ parameter, but needs to check whether it is already combined with `--platform windows`. |We do not have "exe" support for "—distribution“ yet. However, this is different from "RPM" as we do not need min artifact to be a exe. The min artifact can be zip and the final product in assemble can be exe.  |2  |Completed   |Completed   |As for "exe" we need to discuss whether a  standard exe is enough, or do we want to invest into Windows official  installer "msi".  |
|We already have "--distribution" param available in assemble workflow, just need to verify existing functions of "ZIP".    |We already have "--distribution" param available  in assemble workflow, but no support for "EXE" redirection. Need to  add a child class supporting the new distribution.   |2  |Completed   |Completed     |   |
|The generation code should pull the artifacts from the build workflow to a temporary location  |Same   |1  |Completed   |Completed    |   |
|The code will compile the components and also call existing install function to install  plugins on min artifacts   |Same   |1  |Completed  |Completed   |  ETA: 2022/09/16 |
|After installation, the code will execute a tool or  utility to wrap all the content into corresponding distribution format |Same   |1  |Completed  |Completed    | **20220819 Note:** Plugin compilation currently have some issues with the build scripts, the compilation itself seems ok at least on things like common-utils. <br/><br/> ETA: 2022/09/16|
|The code will move the final distribution artifact from the temp location to dist folder   |Same   |1  |Completed  |Completed    |  ETA: 2022/09/07 |


### Note: MSI section in this milestone is obsolete as MSI is just a wrapper of the content in ZIP. So as long as ZIP is completed here MSI is considered complete as well.

- [ ] do not remove

* PRs:

20220715:
* https://github.com/opensearch-project/opensearch-ci/pull/155

20220721:
* https://github.com/opensearch-project/opensearch-ci/pull/167

20220722:
* https://github.com/opensearch-project/opensearch-ci/pull/169

20220819:
* https://github.com/opensearch-project/opensearch-build/pull/2483
* https://github.com/opensearch-project/opensearch-ci/pull/187

20220824:
* https://github.com/opensearch-project/opensearch-ci/pull/190

20220902:
* https://github.com/opensearch-project/opensearch-ci/pull/197
* https://github.com/opensearch-project/opensearch-build/pull/2550

20220907:
* common-utils: https://github.com/opensearch-project/common-utils/issues/238
  * https://github.com/opensearch-project/common-utils/pull/258
* https://github.com/opensearch-project/opensearch-ci/pull/199

20220914:
* alerting: https://github.com/opensearch-project/alerting/issues/557
  * https://github.com/opensearch-project/alerting/issues/573

20220915:
* https://github.com/opensearch-project/opensearch-ci/pull/201

20220916:
* https://github.com/opensearch-project/opensearch-ci/pull/202

20220927:
* https://github.com/opensearch-project/alerting/pull/570
* https://github.com/opensearch-project/alerting/pull/571
* https://github.com/opensearch-project/alerting/pull/572

20220928:
* https://github.com/opensearch-project/opensearch-build/pull/2672
* security: https://github.com/opensearch-project/security/issues/2122
* https://github.com/opensearch-project/common-utils/pull/258

20221004:
* https://github.com/opensearch-project/opensearch-build/pull/2678

20221006:
* Security: https://github.com/opensearch-project/security/issues/2148
* https://github.com/opensearch-project/opensearch-build/pull/2704

20221007:
* OSD: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2535

20221010:
* https://github.com/opensearch-project/opensearch-ci/pull/208
* https://github.com/opensearch-project/opensearch-ci/pull/209

20221011:
* https://github.com/opensearch-project/opensearch-build-libraries/pull/14
* https://github.com/opensearch-project/opensearch-build/pull/2721
* OSD: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2554

20221012:
* https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2026

20221013:
* https://github.com/opensearch-project/opensearch-build/pull/2730

20221018:
* https://github.com/opensearch-project/opensearch-build/pull/2756
* SQL: https://github.com/opensearch-project/sql/issues/928
* https://github.com/opensearch-project/opensearch-build/pull/2761

20221019:
* https://github.com/opensearch-project/OpenSearch/issues/4817

20221024:
* https://github.com/opensearch-project/opensearch-build/pull/2788

20221025:
* OSD: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2669
* https://github.com/opensearch-project/k-NN/pull/595
* https://github.com/opensearch-project/opensearch-ci/pull/212

20221027:
* https://github.com/opensearch-project/opensearch-ci/pull/213
* https://github.com/opensearch-project/opensearch-build/pull/2812
* https://github.com/opensearch-project/opensearch-build/pull/2817
* https://github.com/opensearch-project/opensearch-ci/pull/214

20221028:
* https://github.com/opensearch-project/opensearch-build/pull/2818
* https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2686

20221101:
* https://github.com/opensearch-project/opensearch-build/pull/2840
* https://github.com/opensearch-project/opensearch-build/pull/2841
* https://github.com/opensearch-project/opensearch-ci/pull/215

20221103:
* https://github.com/opensearch-project/opensearch-build/pull/2845
* https://github.com/opensearch-project/opensearch-ci/pull/219
~~* OSD: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2764~~
* OSD ftrepo: https://github.com/opensearch-project/opensearch-dashboards-functional-test/issues/370

20221104:
* https://github.com/opensearch-project/opensearch-build/pull/2848
* https://github.com/opensearch-project/opensearch-ci/pull/224
* https://github.com/opensearch-project/opensearch-build-libraries/pull/28

20221107:
* https://github.com/opensearch-project/opensearch-build/pull/2860
* https://github.com/opensearch-project/opensearch-ci/pull/228

20221108:
* OSD ftrepo: https://github.com/opensearch-project/opensearch-dashboards-functional-test/issues/370
* https://github.com/opensearch-project/opensearch-build-libraries/pull/31
* https://github.com/opensearch-project/opensearch-build/pull/2869

20221114:
* #2892

20221116:
* https://github.com/opensearch-project/opensearch-build/pull/2914

--- 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/dist.py`
Content:
```
1 # Copyright OpenSearch Contributors
2 # SPDX-License-Identifier: Apache-2.0
3 #
4 # The OpenSearch Contributors require contributions made to
5 # this file be licensed under the Apache-2.0 license or a
6 # compatible open source license.
7 
8 import errno
9 import logging
10 import os
11 import shutil
12 import tarfile
13 import zipfile
14 from abc import ABC, abstractmethod
15 
16 from assemble_workflow.bundle_rpm import BundleRpm
17 from manifests.build_manifest import BuildManifest
18 from system.zip_file import ZipFile
19 
20 
21 class Dist(ABC):
22     def __init__(self, name: str, path: str, min_path: str, build_cls: BuildManifest.Build) -> None:
23         self.build_cls = build_cls
24         self.name = name
25         self.filename = name.lower()
26         self.path = path
27         self.min_path = min_path
28 
29     @abstractmethod
30     def __extract__(self, dest: str) -> None:
31         pass
32 
33     @abstractmethod
34     def __build__(self, name: str, dest: str) -> None:
35         pass
36 
37     def find_min_archive_path(self, dest: str) -> str:
38         '''
39         Return the single folder that contains the main files of {name}.
40         This folder is normally in the format of {filename}-{exact or bc version}.
41 
42         Ex: opensearch-1.3.0 or opensearch-dashboards-1.3.0
43 
44         Adding a check of whether {filename} is in folder name is to ensure
45         that only folders in above format are returned.
46 
47         In tar there is only 1 top level folders after extraction.
48         But in rpm there are multiple folders such as var / usr / opensearch-1.3.0 ......
49 
50         This is to ensure corrent folder is found, instead of simply choosing the 1st in the list.
51         '''
52 
53         for file in os.scandir(dest):
54             if self.filename in file.name and file.is_dir():
55                 self.archive_path = file.path
56                 return self.archive_path
57 
58         raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), os.path.join(dest, "*"))
59 
60     def rename_archive_path(self, path: str) -> str:
61         '''
62         Rename the single folder at the top level of the tar that contains the min distribution to match current version.
63         For example, when OpenSearch 1.1.1 is built using the 1.1.0 artifact, we rename opensearch-1.1.0 to opensearch-1.1.1.
64         '''
65         current_name = os.path.basename(path)
66         target_name = self.min_path
67         if current_name != target_name:
68             logging.info(f"Renaming {path} to {target_name}.")
69             target_path = os.path.join(os.path.dirname(path), target_name)
70             os.rename(path, target_path)
71             return target_path
72         else:
73             return path
74 
75     def extract(self, dest: str) -> str:
76         self.__extract__(dest)
77         self.archive_path = self.rename_archive_path(
78             self.find_min_archive_path(dest)
79         )
80         return self.archive_path
81 
82     def build(self, name: str, dest: str) -> None:
83         self.__build__(name, dest)
84         path = os.path.join(dest, name)
85         shutil.copyfile(name, path)
86         logging.info(f"Published {path}.")
87 
88 
89 class DistTar(Dist):
90     def __extract__(self, dest: str) -> None:
91         with tarfile.open(self.path, "r:gz") as tar:
92             tar.extractall(dest)
93 
94     def __build__(self, name: str, dest: str) -> None:
95         with tarfile.open(name, "w:gz") as tar:
96             tar.add(self.archive_path, arcname=os.path.basename(self.archive_path))
97 
98 
99 class DistZip(Dist):
100     def __extract__(self, dest: str) -> None:
101         with ZipFile(self.path, "r") as zip:
102             zip.extractall(dest)
103 
104     def __build__(self, name: str, dest: str) -> None:
105         with ZipFile(name, "w", zipfile.ZIP_DEFLATED) as zip:
106             rootlen = len(self.archive_path) + 1
107             for base, _, files in os.walk(self.archive_path):
108                 for file in files:
109                     fn = os.path.join(base, file)
110                     zip.write(fn, fn[rootlen:])
111 
112 
113 class DistRpm(Dist):
114 
115     def __extract__(self, dest: str) -> None:
116         BundleRpm(self.filename, self.path, self.min_path).extract(dest)
117 
118     def __build__(self, name: str, dest: str) -> None:
119         BundleRpm(self.filename, self.path, self.min_path).build(name, dest, self.archive_path, self.build_cls)
120
```
--- 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()

```

diff --git a/src/assemble_workflow/dist.py b/src/assemble_workflow/dist.py
--- a/src/assemble_workflow/dist.py
+++ b/src/assemble_workflow/dist.py
@@ -103,11 +103,17 @@
 
     def __build__(self, name: str, dest: str) -> None:
         with ZipFile(name, "w", zipfile.ZIP_DEFLATED) as zip:
-            rootlen = len(self.archive_path) + 1
+            # root               : /tmp/tmp********/opensearch-<version+qualifier>
+            # leadingdir         : opensearch-<version+qualifier>
+            # root no leading dir: /tmp/tmp********/
+            # This is to preserve the leading directory `opensearch-<version+qualifier>` in zip
+            rootlen = len(self.archive_path)
+            leadingdirlen = len(os.path.basename(self.archive_path))
+            noleadingdirlen = rootlen - leadingdirlen
             for base, _, files in os.walk(self.archive_path):
                 for file in files:
                     fn = os.path.join(base, file)
-                    zip.write(fn, fn[rootlen:])
+                    zip.write(fn, fn[noleadingdirlen:])
 
 
 class DistRpm(Dist):

{"golden_diff": "diff --git a/src/assemble_workflow/dist.py b/src/assemble_workflow/dist.py\n--- a/src/assemble_workflow/dist.py\n+++ b/src/assemble_workflow/dist.py\n@@ -103,11 +103,17 @@\n \n     def __build__(self, name: str, dest: str) -> None:\n         with ZipFile(name, \"w\", zipfile.ZIP_DEFLATED) as zip:\n-            rootlen = len(self.archive_path) + 1\n+            # root               : /tmp/tmp********/opensearch-<version+qualifier>\n+            # leadingdir         : opensearch-<version+qualifier>\n+            # root no leading dir: /tmp/tmp********/\n+            # This is to preserve the leading directory `opensearch-<version+qualifier>` in zip\n+            rootlen = len(self.archive_path)\n+            leadingdirlen = len(os.path.basename(self.archive_path))\n+            noleadingdirlen = rootlen - leadingdirlen\n             for base, _, files in os.walk(self.archive_path):\n                 for file in files:\n                     fn = os.path.join(base, file)\n-                    zip.write(fn, fn[rootlen:])\n+                    zip.write(fn, fn[noleadingdirlen:])\n \n \n class DistRpm(Dist):\n", "issue": "[WIN M1] WIN (ZIP/MSI) Build/Assemble Process \n|Tasks(ZIP) |**Tasks(MSI)** |Estimate   |Status(ZIP)    |Status(MSI)    |Notes  |\r\n|---    |---    |---    |---    |---    |---    |\r\n|Re-use the existing build process to generate the  OpenSearch/Dashboards min + all of the plugins artifacts for WIN package to  use |Same   |0  |Completed  |Completed  |   |\r\n|The artifacts should be built with ~~LINUX~~ Windows platform  specified, ~~as we will cross-compile WIN binary on LINUX then package with  WINDOWS JDK~~.  |Same   |1  |Completed    |Completed   |~~This is still in debate as we can techinically build WIN  on Windows machine, but there are a lot of things to setup just so Jenkins  can run Python on Windows Agent.~~ We are able to run shell scripts natively on Windows agent  |\r\n|We already have \"zip\" supported for \"\u2014distribution\u201c parameter, but needs to check whether it is already combined with `--platform windows`. |We do not have \"exe\" support for \"\u2014distribution\u201c yet. However, this is different from \"RPM\" as we do not need min artifact to be a exe. The min artifact can be zip and the final product in assemble can be exe.  |2  |Completed   |Completed   |As for \"exe\" we need to discuss whether a  standard exe is enough, or do we want to invest into Windows official  installer \"msi\".  |\r\n|We already have \"--distribution\" param available in assemble workflow, just need to verify existing functions of \"ZIP\".    |We already have \"--distribution\" param available  in assemble workflow, but no support for \"EXE\" redirection. Need to  add a child class supporting the new distribution.   |2  |Completed   |Completed     |   |\r\n|The generation code should pull the artifacts from the build workflow to a temporary location  |Same   |1  |Completed   |Completed    |   |\r\n|The code will compile the components and also call existing install function to install  plugins on min artifacts   |Same   |1  |Completed  |Completed   |  ETA: 2022/09/16 |\r\n|After installation, the code will execute a tool or  utility to wrap all the content into corresponding distribution format |Same   |1  |Completed  |Completed    | **20220819 Note:** Plugin compilation currently have some issues with the build scripts, the compilation itself seems ok at least on things like common-utils. <br/><br/> ETA: 2022/09/16|\r\n|The code will move the final distribution artifact from the temp location to dist folder   |Same   |1  |Completed  |Completed    |  ETA: 2022/09/07 |\r\n\r\n\r\n### Note: MSI section in this milestone is obsolete as MSI is just a wrapper of the content in ZIP. So as long as ZIP is completed here MSI is considered complete as well.\r\n\r\n- [ ] do not remove\r\n\r\n* PRs:\r\n\r\n20220715:\r\n* https://github.com/opensearch-project/opensearch-ci/pull/155\r\n\r\n20220721:\r\n* https://github.com/opensearch-project/opensearch-ci/pull/167\r\n\r\n20220722:\r\n* https://github.com/opensearch-project/opensearch-ci/pull/169\r\n\r\n20220819:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2483\r\n* https://github.com/opensearch-project/opensearch-ci/pull/187\r\n\r\n20220824:\r\n* https://github.com/opensearch-project/opensearch-ci/pull/190\r\n\r\n20220902:\r\n* https://github.com/opensearch-project/opensearch-ci/pull/197\r\n* https://github.com/opensearch-project/opensearch-build/pull/2550\r\n\r\n20220907:\r\n* common-utils: https://github.com/opensearch-project/common-utils/issues/238\r\n  * https://github.com/opensearch-project/common-utils/pull/258\r\n* https://github.com/opensearch-project/opensearch-ci/pull/199\r\n\r\n20220914:\r\n* alerting: https://github.com/opensearch-project/alerting/issues/557\r\n  * https://github.com/opensearch-project/alerting/issues/573\r\n\r\n20220915:\r\n* https://github.com/opensearch-project/opensearch-ci/pull/201\r\n\r\n20220916:\r\n* https://github.com/opensearch-project/opensearch-ci/pull/202\r\n\r\n20220927:\r\n* https://github.com/opensearch-project/alerting/pull/570\r\n* https://github.com/opensearch-project/alerting/pull/571\r\n* https://github.com/opensearch-project/alerting/pull/572\r\n\r\n20220928:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2672\r\n* security: https://github.com/opensearch-project/security/issues/2122\r\n* https://github.com/opensearch-project/common-utils/pull/258\r\n\r\n20221004:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2678\r\n\r\n20221006:\r\n* Security: https://github.com/opensearch-project/security/issues/2148\r\n* https://github.com/opensearch-project/opensearch-build/pull/2704\r\n\r\n20221007:\r\n* OSD: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2535\r\n\r\n20221010:\r\n* https://github.com/opensearch-project/opensearch-ci/pull/208\r\n* https://github.com/opensearch-project/opensearch-ci/pull/209\r\n\r\n20221011:\r\n* https://github.com/opensearch-project/opensearch-build-libraries/pull/14\r\n* https://github.com/opensearch-project/opensearch-build/pull/2721\r\n* OSD: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2554\r\n\r\n20221012:\r\n* https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2026\r\n\r\n20221013:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2730\r\n\r\n20221018:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2756\r\n* SQL: https://github.com/opensearch-project/sql/issues/928\r\n* https://github.com/opensearch-project/opensearch-build/pull/2761\r\n\r\n20221019:\r\n* https://github.com/opensearch-project/OpenSearch/issues/4817\r\n\r\n20221024:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2788\r\n\r\n20221025:\r\n* OSD: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2669\r\n* https://github.com/opensearch-project/k-NN/pull/595\r\n* https://github.com/opensearch-project/opensearch-ci/pull/212\r\n\r\n20221027:\r\n* https://github.com/opensearch-project/opensearch-ci/pull/213\r\n* https://github.com/opensearch-project/opensearch-build/pull/2812\r\n* https://github.com/opensearch-project/opensearch-build/pull/2817\r\n* https://github.com/opensearch-project/opensearch-ci/pull/214\r\n\r\n20221028:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2818\r\n* https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2686\r\n\r\n20221101:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2840\r\n* https://github.com/opensearch-project/opensearch-build/pull/2841\r\n* https://github.com/opensearch-project/opensearch-ci/pull/215\r\n\r\n20221103:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2845\r\n* https://github.com/opensearch-project/opensearch-ci/pull/219\r\n~~* OSD: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2764~~\r\n* OSD ftrepo: https://github.com/opensearch-project/opensearch-dashboards-functional-test/issues/370\r\n\r\n20221104:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2848\r\n* https://github.com/opensearch-project/opensearch-ci/pull/224\r\n* https://github.com/opensearch-project/opensearch-build-libraries/pull/28\r\n\r\n20221107:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2860\r\n* https://github.com/opensearch-project/opensearch-ci/pull/228\r\n\r\n20221108:\r\n* OSD ftrepo: https://github.com/opensearch-project/opensearch-dashboards-functional-test/issues/370\r\n* https://github.com/opensearch-project/opensearch-build-libraries/pull/31\r\n* https://github.com/opensearch-project/opensearch-build/pull/2869\r\n\r\n20221114:\r\n* #2892\r\n\r\n20221116:\r\n* https://github.com/opensearch-project/opensearch-build/pull/2914\n", "before_files": [{"content": "# Copyright OpenSearch Contributors\n# 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 errno\nimport logging\nimport os\nimport shutil\nimport tarfile\nimport zipfile\nfrom abc import ABC, abstractmethod\n\nfrom assemble_workflow.bundle_rpm import BundleRpm\nfrom manifests.build_manifest import BuildManifest\nfrom system.zip_file import ZipFile\n\n\nclass Dist(ABC):\n    def __init__(self, name: str, path: str, min_path: str, build_cls: BuildManifest.Build) -> None:\n        self.build_cls = build_cls\n        self.name = name\n        self.filename = name.lower()\n        self.path = path\n        self.min_path = min_path\n\n    @abstractmethod\n    def __extract__(self, dest: str) -> None:\n        pass\n\n    @abstractmethod\n    def __build__(self, name: str, dest: str) -> None:\n        pass\n\n    def find_min_archive_path(self, dest: str) -> str:\n        '''\n        Return the single folder that contains the main files of {name}.\n        This folder is normally in the format of {filename}-{exact or bc version}.\n\n        Ex: opensearch-1.3.0 or opensearch-dashboards-1.3.0\n\n        Adding a check of whether {filename} is in folder name is to ensure\n        that only folders in above format are returned.\n\n        In tar there is only 1 top level folders after extraction.\n        But in rpm there are multiple folders such as var / usr / opensearch-1.3.0 ......\n\n        This is to ensure corrent folder is found, instead of simply choosing the 1st in the list.\n        '''\n\n        for file in os.scandir(dest):\n            if self.filename in file.name and file.is_dir():\n                self.archive_path = file.path\n                return self.archive_path\n\n        raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), os.path.join(dest, \"*\"))\n\n    def rename_archive_path(self, path: str) -> str:\n        '''\n        Rename the single folder at the top level of the tar that contains the min distribution to match current version.\n        For example, when OpenSearch 1.1.1 is built using the 1.1.0 artifact, we rename opensearch-1.1.0 to opensearch-1.1.1.\n        '''\n        current_name = os.path.basename(path)\n        target_name = self.min_path\n        if current_name != target_name:\n            logging.info(f\"Renaming {path} to {target_name}.\")\n            target_path = os.path.join(os.path.dirname(path), target_name)\n            os.rename(path, target_path)\n            return target_path\n        else:\n            return path\n\n    def extract(self, dest: str) -> str:\n        self.__extract__(dest)\n        self.archive_path = self.rename_archive_path(\n            self.find_min_archive_path(dest)\n        )\n        return self.archive_path\n\n    def build(self, name: str, dest: str) -> None:\n        self.__build__(name, dest)\n        path = os.path.join(dest, name)\n        shutil.copyfile(name, path)\n        logging.info(f\"Published {path}.\")\n\n\nclass DistTar(Dist):\n    def __extract__(self, dest: str) -> None:\n        with tarfile.open(self.path, \"r:gz\") as tar:\n            tar.extractall(dest)\n\n    def __build__(self, name: str, dest: str) -> None:\n        with tarfile.open(name, \"w:gz\") as tar:\n            tar.add(self.archive_path, arcname=os.path.basename(self.archive_path))\n\n\nclass DistZip(Dist):\n    def __extract__(self, dest: str) -> None:\n        with ZipFile(self.path, \"r\") as zip:\n            zip.extractall(dest)\n\n    def __build__(self, name: str, dest: str) -> None:\n        with ZipFile(name, \"w\", zipfile.ZIP_DEFLATED) as zip:\n            rootlen = len(self.archive_path) + 1\n            for base, _, files in os.walk(self.archive_path):\n                for file in files:\n                    fn = os.path.join(base, file)\n                    zip.write(fn, fn[rootlen:])\n\n\nclass DistRpm(Dist):\n\n    def __extract__(self, dest: str) -> None:\n        BundleRpm(self.filename, self.path, self.min_path).extract(dest)\n\n    def __build__(self, name: str, dest: str) -> None:\n        BundleRpm(self.filename, self.path, self.min_path).build(name, dest, self.archive_path, self.build_cls)\n", "path": "src/assemble_workflow/dist.py"}], "after_files": [{"content": "# Copyright OpenSearch Contributors\n# 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 errno\nimport logging\nimport os\nimport shutil\nimport tarfile\nimport zipfile\nfrom abc import ABC, abstractmethod\n\nfrom assemble_workflow.bundle_rpm import BundleRpm\nfrom manifests.build_manifest import BuildManifest\nfrom system.zip_file import ZipFile\n\n\nclass Dist(ABC):\n    def __init__(self, name: str, path: str, min_path: str, build_cls: BuildManifest.Build) -> None:\n        self.build_cls = build_cls\n        self.name = name\n        self.filename = name.lower()\n        self.path = path\n        self.min_path = min_path\n\n    @abstractmethod\n    def __extract__(self, dest: str) -> None:\n        pass\n\n    @abstractmethod\n    def __build__(self, name: str, dest: str) -> None:\n        pass\n\n    def find_min_archive_path(self, dest: str) -> str:\n        '''\n        Return the single folder that contains the main files of {name}.\n        This folder is normally in the format of {filename}-{exact or bc version}.\n\n        Ex: opensearch-1.3.0 or opensearch-dashboards-1.3.0\n\n        Adding a check of whether {filename} is in folder name is to ensure\n        that only folders in above format are returned.\n\n        In tar there is only 1 top level folders after extraction.\n        But in rpm there are multiple folders such as var / usr / opensearch-1.3.0 ......\n\n        This is to ensure corrent folder is found, instead of simply choosing the 1st in the list.\n        '''\n\n        for file in os.scandir(dest):\n            if self.filename in file.name and file.is_dir():\n                self.archive_path = file.path\n                return self.archive_path\n\n        raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), os.path.join(dest, \"*\"))\n\n    def rename_archive_path(self, path: str) -> str:\n        '''\n        Rename the single folder at the top level of the tar that contains the min distribution to match current version.\n        For example, when OpenSearch 1.1.1 is built using the 1.1.0 artifact, we rename opensearch-1.1.0 to opensearch-1.1.1.\n        '''\n        current_name = os.path.basename(path)\n        target_name = self.min_path\n        if current_name != target_name:\n            logging.info(f\"Renaming {path} to {target_name}.\")\n            target_path = os.path.join(os.path.dirname(path), target_name)\n            os.rename(path, target_path)\n            return target_path\n        else:\n            return path\n\n    def extract(self, dest: str) -> str:\n        self.__extract__(dest)\n        self.archive_path = self.rename_archive_path(\n            self.find_min_archive_path(dest)\n        )\n        return self.archive_path\n\n    def build(self, name: str, dest: str) -> None:\n        self.__build__(name, dest)\n        path = os.path.join(dest, name)\n        shutil.copyfile(name, path)\n        logging.info(f\"Published {path}.\")\n\n\nclass DistTar(Dist):\n    def __extract__(self, dest: str) -> None:\n        with tarfile.open(self.path, \"r:gz\") as tar:\n            tar.extractall(dest)\n\n    def __build__(self, name: str, dest: str) -> None:\n        with tarfile.open(name, \"w:gz\") as tar:\n            tar.add(self.archive_path, arcname=os.path.basename(self.archive_path))\n\n\nclass DistZip(Dist):\n    def __extract__(self, dest: str) -> None:\n        with ZipFile(self.path, \"r\") as zip:\n            zip.extractall(dest)\n\n    def __build__(self, name: str, dest: str) -> None:\n        with ZipFile(name, \"w\", zipfile.ZIP_DEFLATED) as zip:\n            # root               : /tmp/tmp********/opensearch-<version+qualifier>\n            # leadingdir         : opensearch-<version+qualifier>\n            # root no leading dir: /tmp/tmp********/\n            # This is to preserve the leading directory `opensearch-<version+qualifier>` in zip\n            rootlen = len(self.archive_path)\n            leadingdirlen = len(os.path.basename(self.archive_path))\n            noleadingdirlen = rootlen - leadingdirlen\n            for base, _, files in os.walk(self.archive_path):\n                for file in files:\n                    fn = os.path.join(base, file)\n                    zip.write(fn, fn[noleadingdirlen:])\n\n\nclass DistRpm(Dist):\n\n    def __extract__(self, dest: str) -> None:\n        BundleRpm(self.filename, self.path, self.min_path).extract(dest)\n\n    def __build__(self, name: str, dest: str) -> None:\n        BundleRpm(self.filename, self.path, self.min_path).build(name, dest, self.archive_path, self.build_cls)\n", "path": "src/assemble_workflow/dist.py"}]}