We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[Enhancement] OS/OSD runner is not separated in manifest_workflow and reduce runtime
We want OS and OSD runner to be separated when workflow create the new manifest file.
As of now both os and osd runner are pointing to the same one.
There are different configurations between the two runners.

The workflow also runs for way too long.
gradle properties does not require a gradle mavelocal to run beforehand.

Plus, gradle 7.x cannot build OS 1.0 anyway and will fail the workflow in the middle.

Thanks.


--- 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/manifests_workflow/component_opensearch_min.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 from typing import Any, List
8 
9 from git.git_repository import GitRepository
10 from manifests_workflow.component import Component
11 from manifests_workflow.component_opensearch import ComponentOpenSearch
12 from system.properties_file import PropertiesFile
13 
14 
15 class ComponentOpenSearchMin(Component):
16     def __init__(self, repo: GitRepository, snapshot: bool = False) -> None:
17         super().__init__(
18             "OpenSearch",
19             repo,
20             snapshot,
21             ["gradle:publish", "gradle:properties:version"],
22         )
23 
24     @classmethod
25     def branches(self, url: str = "https://github.com/opensearch-project/OpenSearch.git") -> List[str]:
26         return Component.branches(url)
27 
28     @classmethod
29     def checkout(self, path: str, branch: str = "main", snapshot: bool = False) -> 'ComponentOpenSearchMin':
30         return ComponentOpenSearchMin(
31             GitRepository("https://github.com/opensearch-project/OpenSearch.git", branch, path),
32             snapshot,
33         )
34 
35     def publish_to_maven_local(self) -> None:
36         cmd = ComponentOpenSearch.gradle_cmd("publishToMavenLocal", {"build.snapshot": str(self.snapshot).lower()})
37         self.git_repo.execute_silent(cmd)
38 
39     @property
40     def properties(self) -> PropertiesFile:
41         cmd = ComponentOpenSearch.gradle_cmd("properties", {"build.snapshot": str(self.snapshot).lower()})
42         return PropertiesFile(self.git_repo.output(cmd))
43 
44     @property
45     def version(self) -> Any:
46         self.publish_to_maven_local()
47         return self.properties.get_value("version")
48
```

Path: `src/manifests_workflow/input_manifests.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 glob
8 import logging
9 import os
10 import re
11 from abc import abstractmethod
12 from typing import Dict, List, Type, Union
13 
14 from manifests.input_manifest import InputManifest
15 from manifests.manifests import Manifests
16 from manifests_workflow.component_opensearch import ComponentOpenSearch
17 from manifests_workflow.component_opensearch_dashboards_min import ComponentOpenSearchDashboardsMin
18 from manifests_workflow.component_opensearch_min import ComponentOpenSearchMin
19 from system.temporary_directory import TemporaryDirectory
20 
21 
22 class InputManifests(Manifests):
23     def __init__(self, name: str) -> None:
24         self.name = name
25         self.prefix = name.lower().replace(" ", "-")
26         super().__init__(InputManifest, InputManifests.files(self.prefix))
27 
28     @classmethod
29     def manifests_path(self) -> str:
30         return os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..", "manifests"))
31 
32     @classmethod
33     def jenkins_path(self) -> str:
34         return os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..", "jenkins"))
35 
36     @classmethod
37     def cron_jenkinsfile(self) -> str:
38         return os.path.join(self.jenkins_path(), "check-for-build.jenkinsfile")
39 
40     @classmethod
41     def files(self, name: str) -> List:
42         results = []
43         for filename in glob.glob(os.path.join(self.manifests_path(), f"**/{name}-*.yml")):
44             # avoids the -maven manifest
45             match = re.search(rf"^{name}-([0-9.]*).yml$", os.path.basename(filename))
46             if match:
47                 results.append(filename)
48         return results
49 
50     @abstractmethod
51     def update(self, min_klass: Union[Type[ComponentOpenSearchMin], Type[ComponentOpenSearchDashboardsMin]], component_klass: Type[ComponentOpenSearch], keep: bool = False) -> None:
52         known_versions = self.versions
53         logging.info(f"Known versions: {known_versions}")
54         main_versions: Dict = {}
55         with TemporaryDirectory(keep=keep, chdir=True) as work_dir:
56             logging.info(f"Checking out components into {work_dir.name}")
57 
58             # check out and build #main, 1.x, etc.
59             branches = min_klass.branches()
60 
61             logging.info(f"Checking {self.name} {branches} branches")
62             for branch in branches:
63                 c = min_klass.checkout(
64                     path=os.path.join(work_dir.name, self.name.replace(" ", ""), branch),
65                     branch=branch,
66                 )
67 
68                 version = c.version
69                 logging.info(f"{self.name}#{branch} is version {version}")
70                 if version not in main_versions.keys():
71                     main_versions[version] = [c]
72 
73             if component_klass is not None:
74                 # components can increment their own version first without incrementing min
75                 manifest = self.latest
76                 logging.info(f"Examining components in the latest manifest of {manifest.build.name} ({manifest.build.version})")
77                 for component in manifest.components.values():
78                     if component.name == self.name:
79                         continue
80 
81                     logging.info(f"Checking out {component.name}#main")
82                     component = component_klass.checkout(
83                         name=component.name,
84                         path=os.path.join(work_dir.name, component.name),
85                         opensearch_version=manifest.build.version,
86                         branch="main",
87                     )
88 
89                     component_version = component.version
90                     if component_version:
91                         release_version = ".".join(component_version.split(".")[:3])
92                         if release_version not in main_versions.keys():
93                             main_versions[release_version] = []
94                         main_versions[release_version].append(component)
95                         logging.info(f"{component.name}#main is version {release_version} (from {component_version})")
96 
97             # summarize
98             logging.info("Found versions on main:")
99             for main_version in main_versions.keys():
100                 for component in main_versions[main_version]:
101                     logging.info(f" {component.name}={main_version}")
102 
103             # generate new manifests
104             for release_version in sorted(main_versions.keys() - known_versions):
105                 self.write_manifest(release_version, main_versions[release_version])
106                 self.add_to_cron(release_version)
107 
108     def create_manifest(self, version: str, components: List = []) -> InputManifest:
109         data: Dict = {
110             "schema-version": "1.0",
111             "build": {
112                 "name": self.name,
113                 "version": version
114             },
115             "ci": {
116                 "image": {
117                     "name": "opensearchstaging/ci-runner:ci-runner-centos7-v1"
118                 }
119             },
120             "components": [],
121         }
122 
123         for component in components:
124             logging.info(f" Adding {component.name}")
125             data["components"].append(component.to_dict())
126 
127         return InputManifest(data)
128 
129     def write_manifest(self, version: str, components: List = []) -> None:
130         logging.info(f"Creating new version: {version}")
131         manifest = self.create_manifest(version, components)
132         manifest_dir = os.path.join(self.manifests_path(), version)
133         os.makedirs(manifest_dir, exist_ok=True)
134         manifest_path = os.path.join(manifest_dir, f"{self.prefix}-{version}.yml")
135         manifest.to_file(manifest_path)
136         logging.info(f"Wrote {manifest_path}")
137 
138     def add_to_cron(self, version: str) -> None:
139         logging.info(f"Adding new version to cron: {version}")
140         jenkinsfile = self.cron_jenkinsfile()
141         with open(jenkinsfile, "r") as f:
142             data = f.read()
143 
144         cron_entry = f"H/10 * * * * %INPUT_MANIFEST={version}/{self.prefix}-{version}.yml;TARGET_JOB_NAME=distribution-build-{self.prefix}\n"
145 
146         if cron_entry in data:
147             raise ValueError(f"{jenkinsfile} already contains an entry for {self.prefix} {version}")
148 
149         data = data.replace(
150             "parameterizedCron '''\n",
151             f"parameterizedCron '''\n{' ' * 12}{cron_entry}"
152         )
153 
154         with open(jenkinsfile, "w") as f:
155             f.write(data)
156 
157         logging.info(f"Wrote {jenkinsfile}")
158
```
--- 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/manifests_workflow/component_opensearch_min.py b/src/manifests_workflow/component_opensearch_min.py
--- a/src/manifests_workflow/component_opensearch_min.py
+++ b/src/manifests_workflow/component_opensearch_min.py
@@ -32,10 +32,6 @@
             snapshot,
         )
 
-    def publish_to_maven_local(self) -> None:
-        cmd = ComponentOpenSearch.gradle_cmd("publishToMavenLocal", {"build.snapshot": str(self.snapshot).lower()})
-        self.git_repo.execute_silent(cmd)
-
     @property
     def properties(self) -> PropertiesFile:
         cmd = ComponentOpenSearch.gradle_cmd("properties", {"build.snapshot": str(self.snapshot).lower()})
@@ -43,5 +39,4 @@
 
     @property
     def version(self) -> Any:
-        self.publish_to_maven_local()
         return self.properties.get_value("version")
diff --git a/src/manifests_workflow/input_manifests.py b/src/manifests_workflow/input_manifests.py
--- a/src/manifests_workflow/input_manifests.py
+++ b/src/manifests_workflow/input_manifests.py
@@ -106,6 +106,11 @@
                 self.add_to_cron(release_version)
 
     def create_manifest(self, version: str, components: List = []) -> InputManifest:
+        image_map = {
+            "opensearch": "opensearchstaging/ci-runner:ci-runner-centos7-opensearch-build-v1",
+            "opensearch-dashboards": "opensearchstaging/ci-runner:ci-runner-centos7-opensearch-dashboards-build-v1"
+        }
+
         data: Dict = {
             "schema-version": "1.0",
             "build": {
@@ -114,7 +119,7 @@
             },
             "ci": {
                 "image": {
-                    "name": "opensearchstaging/ci-runner:ci-runner-centos7-v1"
+                    "name": image_map[self.prefix]
                 }
             },
             "components": [],

{"golden_diff": "diff --git a/src/manifests_workflow/component_opensearch_min.py b/src/manifests_workflow/component_opensearch_min.py\n--- a/src/manifests_workflow/component_opensearch_min.py\n+++ b/src/manifests_workflow/component_opensearch_min.py\n@@ -32,10 +32,6 @@\n             snapshot,\n         )\n \n-    def publish_to_maven_local(self) -> None:\n-        cmd = ComponentOpenSearch.gradle_cmd(\"publishToMavenLocal\", {\"build.snapshot\": str(self.snapshot).lower()})\n-        self.git_repo.execute_silent(cmd)\n-\n     @property\n     def properties(self) -> PropertiesFile:\n         cmd = ComponentOpenSearch.gradle_cmd(\"properties\", {\"build.snapshot\": str(self.snapshot).lower()})\n@@ -43,5 +39,4 @@\n \n     @property\n     def version(self) -> Any:\n-        self.publish_to_maven_local()\n         return self.properties.get_value(\"version\")\ndiff --git a/src/manifests_workflow/input_manifests.py b/src/manifests_workflow/input_manifests.py\n--- a/src/manifests_workflow/input_manifests.py\n+++ b/src/manifests_workflow/input_manifests.py\n@@ -106,6 +106,11 @@\n                 self.add_to_cron(release_version)\n \n     def create_manifest(self, version: str, components: List = []) -> InputManifest:\n+        image_map = {\n+            \"opensearch\": \"opensearchstaging/ci-runner:ci-runner-centos7-opensearch-build-v1\",\n+            \"opensearch-dashboards\": \"opensearchstaging/ci-runner:ci-runner-centos7-opensearch-dashboards-build-v1\"\n+        }\n+\n         data: Dict = {\n             \"schema-version\": \"1.0\",\n             \"build\": {\n@@ -114,7 +119,7 @@\n             },\n             \"ci\": {\n                 \"image\": {\n-                    \"name\": \"opensearchstaging/ci-runner:ci-runner-centos7-v1\"\n+                    \"name\": image_map[self.prefix]\n                 }\n             },\n             \"components\": [],\n", "issue": "[Enhancement] OS/OSD runner is not separated in manifest_workflow and reduce runtime\nWe want OS and OSD runner to be separated when workflow create the new manifest file.\r\nAs of now both os and osd runner are pointing to the same one.\r\nThere are different configurations between the two runners.\r\n\r\nThe workflow also runs for way too long.\r\ngradle properties does not require a gradle mavelocal to run beforehand.\r\n\r\nPlus, gradle 7.x cannot build OS 1.0 anyway and will fail the workflow in the middle.\r\n\r\nThanks.\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\nfrom typing import Any, List\n\nfrom git.git_repository import GitRepository\nfrom manifests_workflow.component import Component\nfrom manifests_workflow.component_opensearch import ComponentOpenSearch\nfrom system.properties_file import PropertiesFile\n\n\nclass ComponentOpenSearchMin(Component):\n    def __init__(self, repo: GitRepository, snapshot: bool = False) -> None:\n        super().__init__(\n            \"OpenSearch\",\n            repo,\n            snapshot,\n            [\"gradle:publish\", \"gradle:properties:version\"],\n        )\n\n    @classmethod\n    def branches(self, url: str = \"https://github.com/opensearch-project/OpenSearch.git\") -> List[str]:\n        return Component.branches(url)\n\n    @classmethod\n    def checkout(self, path: str, branch: str = \"main\", snapshot: bool = False) -> 'ComponentOpenSearchMin':\n        return ComponentOpenSearchMin(\n            GitRepository(\"https://github.com/opensearch-project/OpenSearch.git\", branch, path),\n            snapshot,\n        )\n\n    def publish_to_maven_local(self) -> None:\n        cmd = ComponentOpenSearch.gradle_cmd(\"publishToMavenLocal\", {\"build.snapshot\": str(self.snapshot).lower()})\n        self.git_repo.execute_silent(cmd)\n\n    @property\n    def properties(self) -> PropertiesFile:\n        cmd = ComponentOpenSearch.gradle_cmd(\"properties\", {\"build.snapshot\": str(self.snapshot).lower()})\n        return PropertiesFile(self.git_repo.output(cmd))\n\n    @property\n    def version(self) -> Any:\n        self.publish_to_maven_local()\n        return self.properties.get_value(\"version\")\n", "path": "src/manifests_workflow/component_opensearch_min.py"}, {"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 glob\nimport logging\nimport os\nimport re\nfrom abc import abstractmethod\nfrom typing import Dict, List, Type, Union\n\nfrom manifests.input_manifest import InputManifest\nfrom manifests.manifests import Manifests\nfrom manifests_workflow.component_opensearch import ComponentOpenSearch\nfrom manifests_workflow.component_opensearch_dashboards_min import ComponentOpenSearchDashboardsMin\nfrom manifests_workflow.component_opensearch_min import ComponentOpenSearchMin\nfrom system.temporary_directory import TemporaryDirectory\n\n\nclass InputManifests(Manifests):\n    def __init__(self, name: str) -> None:\n        self.name = name\n        self.prefix = name.lower().replace(\" \", \"-\")\n        super().__init__(InputManifest, InputManifests.files(self.prefix))\n\n    @classmethod\n    def manifests_path(self) -> str:\n        return os.path.realpath(os.path.join(os.path.dirname(__file__), \"..\", \"..\", \"manifests\"))\n\n    @classmethod\n    def jenkins_path(self) -> str:\n        return os.path.realpath(os.path.join(os.path.dirname(__file__), \"..\", \"..\", \"jenkins\"))\n\n    @classmethod\n    def cron_jenkinsfile(self) -> str:\n        return os.path.join(self.jenkins_path(), \"check-for-build.jenkinsfile\")\n\n    @classmethod\n    def files(self, name: str) -> List:\n        results = []\n        for filename in glob.glob(os.path.join(self.manifests_path(), f\"**/{name}-*.yml\")):\n            # avoids the -maven manifest\n            match = re.search(rf\"^{name}-([0-9.]*).yml$\", os.path.basename(filename))\n            if match:\n                results.append(filename)\n        return results\n\n    @abstractmethod\n    def update(self, min_klass: Union[Type[ComponentOpenSearchMin], Type[ComponentOpenSearchDashboardsMin]], component_klass: Type[ComponentOpenSearch], keep: bool = False) -> None:\n        known_versions = self.versions\n        logging.info(f\"Known versions: {known_versions}\")\n        main_versions: Dict = {}\n        with TemporaryDirectory(keep=keep, chdir=True) as work_dir:\n            logging.info(f\"Checking out components into {work_dir.name}\")\n\n            # check out and build #main, 1.x, etc.\n            branches = min_klass.branches()\n\n            logging.info(f\"Checking {self.name} {branches} branches\")\n            for branch in branches:\n                c = min_klass.checkout(\n                    path=os.path.join(work_dir.name, self.name.replace(\" \", \"\"), branch),\n                    branch=branch,\n                )\n\n                version = c.version\n                logging.info(f\"{self.name}#{branch} is version {version}\")\n                if version not in main_versions.keys():\n                    main_versions[version] = [c]\n\n            if component_klass is not None:\n                # components can increment their own version first without incrementing min\n                manifest = self.latest\n                logging.info(f\"Examining components in the latest manifest of {manifest.build.name} ({manifest.build.version})\")\n                for component in manifest.components.values():\n                    if component.name == self.name:\n                        continue\n\n                    logging.info(f\"Checking out {component.name}#main\")\n                    component = component_klass.checkout(\n                        name=component.name,\n                        path=os.path.join(work_dir.name, component.name),\n                        opensearch_version=manifest.build.version,\n                        branch=\"main\",\n                    )\n\n                    component_version = component.version\n                    if component_version:\n                        release_version = \".\".join(component_version.split(\".\")[:3])\n                        if release_version not in main_versions.keys():\n                            main_versions[release_version] = []\n                        main_versions[release_version].append(component)\n                        logging.info(f\"{component.name}#main is version {release_version} (from {component_version})\")\n\n            # summarize\n            logging.info(\"Found versions on main:\")\n            for main_version in main_versions.keys():\n                for component in main_versions[main_version]:\n                    logging.info(f\" {component.name}={main_version}\")\n\n            # generate new manifests\n            for release_version in sorted(main_versions.keys() - known_versions):\n                self.write_manifest(release_version, main_versions[release_version])\n                self.add_to_cron(release_version)\n\n    def create_manifest(self, version: str, components: List = []) -> InputManifest:\n        data: Dict = {\n            \"schema-version\": \"1.0\",\n            \"build\": {\n                \"name\": self.name,\n                \"version\": version\n            },\n            \"ci\": {\n                \"image\": {\n                    \"name\": \"opensearchstaging/ci-runner:ci-runner-centos7-v1\"\n                }\n            },\n            \"components\": [],\n        }\n\n        for component in components:\n            logging.info(f\" Adding {component.name}\")\n            data[\"components\"].append(component.to_dict())\n\n        return InputManifest(data)\n\n    def write_manifest(self, version: str, components: List = []) -> None:\n        logging.info(f\"Creating new version: {version}\")\n        manifest = self.create_manifest(version, components)\n        manifest_dir = os.path.join(self.manifests_path(), version)\n        os.makedirs(manifest_dir, exist_ok=True)\n        manifest_path = os.path.join(manifest_dir, f\"{self.prefix}-{version}.yml\")\n        manifest.to_file(manifest_path)\n        logging.info(f\"Wrote {manifest_path}\")\n\n    def add_to_cron(self, version: str) -> None:\n        logging.info(f\"Adding new version to cron: {version}\")\n        jenkinsfile = self.cron_jenkinsfile()\n        with open(jenkinsfile, \"r\") as f:\n            data = f.read()\n\n        cron_entry = f\"H/10 * * * * %INPUT_MANIFEST={version}/{self.prefix}-{version}.yml;TARGET_JOB_NAME=distribution-build-{self.prefix}\\n\"\n\n        if cron_entry in data:\n            raise ValueError(f\"{jenkinsfile} already contains an entry for {self.prefix} {version}\")\n\n        data = data.replace(\n            \"parameterizedCron '''\\n\",\n            f\"parameterizedCron '''\\n{' ' * 12}{cron_entry}\"\n        )\n\n        with open(jenkinsfile, \"w\") as f:\n            f.write(data)\n\n        logging.info(f\"Wrote {jenkinsfile}\")\n", "path": "src/manifests_workflow/input_manifests.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\nfrom typing import Any, List\n\nfrom git.git_repository import GitRepository\nfrom manifests_workflow.component import Component\nfrom manifests_workflow.component_opensearch import ComponentOpenSearch\nfrom system.properties_file import PropertiesFile\n\n\nclass ComponentOpenSearchMin(Component):\n    def __init__(self, repo: GitRepository, snapshot: bool = False) -> None:\n        super().__init__(\n            \"OpenSearch\",\n            repo,\n            snapshot,\n            [\"gradle:publish\", \"gradle:properties:version\"],\n        )\n\n    @classmethod\n    def branches(self, url: str = \"https://github.com/opensearch-project/OpenSearch.git\") -> List[str]:\n        return Component.branches(url)\n\n    @classmethod\n    def checkout(self, path: str, branch: str = \"main\", snapshot: bool = False) -> 'ComponentOpenSearchMin':\n        return ComponentOpenSearchMin(\n            GitRepository(\"https://github.com/opensearch-project/OpenSearch.git\", branch, path),\n            snapshot,\n        )\n\n    @property\n    def properties(self) -> PropertiesFile:\n        cmd = ComponentOpenSearch.gradle_cmd(\"properties\", {\"build.snapshot\": str(self.snapshot).lower()})\n        return PropertiesFile(self.git_repo.output(cmd))\n\n    @property\n    def version(self) -> Any:\n        return self.properties.get_value(\"version\")\n", "path": "src/manifests_workflow/component_opensearch_min.py"}, {"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 glob\nimport logging\nimport os\nimport re\nfrom abc import abstractmethod\nfrom typing import Dict, List, Type, Union\n\nfrom manifests.input_manifest import InputManifest\nfrom manifests.manifests import Manifests\nfrom manifests_workflow.component_opensearch import ComponentOpenSearch\nfrom manifests_workflow.component_opensearch_dashboards_min import ComponentOpenSearchDashboardsMin\nfrom manifests_workflow.component_opensearch_min import ComponentOpenSearchMin\nfrom system.temporary_directory import TemporaryDirectory\n\n\nclass InputManifests(Manifests):\n    def __init__(self, name: str) -> None:\n        self.name = name\n        self.prefix = name.lower().replace(\" \", \"-\")\n        super().__init__(InputManifest, InputManifests.files(self.prefix))\n\n    @classmethod\n    def manifests_path(self) -> str:\n        return os.path.realpath(os.path.join(os.path.dirname(__file__), \"..\", \"..\", \"manifests\"))\n\n    @classmethod\n    def jenkins_path(self) -> str:\n        return os.path.realpath(os.path.join(os.path.dirname(__file__), \"..\", \"..\", \"jenkins\"))\n\n    @classmethod\n    def cron_jenkinsfile(self) -> str:\n        return os.path.join(self.jenkins_path(), \"check-for-build.jenkinsfile\")\n\n    @classmethod\n    def files(self, name: str) -> List:\n        results = []\n        for filename in glob.glob(os.path.join(self.manifests_path(), f\"**/{name}-*.yml\")):\n            # avoids the -maven manifest\n            match = re.search(rf\"^{name}-([0-9.]*).yml$\", os.path.basename(filename))\n            if match:\n                results.append(filename)\n        return results\n\n    @abstractmethod\n    def update(self, min_klass: Union[Type[ComponentOpenSearchMin], Type[ComponentOpenSearchDashboardsMin]], component_klass: Type[ComponentOpenSearch], keep: bool = False) -> None:\n        known_versions = self.versions\n        logging.info(f\"Known versions: {known_versions}\")\n        main_versions: Dict = {}\n        with TemporaryDirectory(keep=keep, chdir=True) as work_dir:\n            logging.info(f\"Checking out components into {work_dir.name}\")\n\n            # check out and build #main, 1.x, etc.\n            branches = min_klass.branches()\n\n            logging.info(f\"Checking {self.name} {branches} branches\")\n            for branch in branches:\n                c = min_klass.checkout(\n                    path=os.path.join(work_dir.name, self.name.replace(\" \", \"\"), branch),\n                    branch=branch,\n                )\n\n                version = c.version\n                logging.info(f\"{self.name}#{branch} is version {version}\")\n                if version not in main_versions.keys():\n                    main_versions[version] = [c]\n\n            if component_klass is not None:\n                # components can increment their own version first without incrementing min\n                manifest = self.latest\n                logging.info(f\"Examining components in the latest manifest of {manifest.build.name} ({manifest.build.version})\")\n                for component in manifest.components.values():\n                    if component.name == self.name:\n                        continue\n\n                    logging.info(f\"Checking out {component.name}#main\")\n                    component = component_klass.checkout(\n                        name=component.name,\n                        path=os.path.join(work_dir.name, component.name),\n                        opensearch_version=manifest.build.version,\n                        branch=\"main\",\n                    )\n\n                    component_version = component.version\n                    if component_version:\n                        release_version = \".\".join(component_version.split(\".\")[:3])\n                        if release_version not in main_versions.keys():\n                            main_versions[release_version] = []\n                        main_versions[release_version].append(component)\n                        logging.info(f\"{component.name}#main is version {release_version} (from {component_version})\")\n\n            # summarize\n            logging.info(\"Found versions on main:\")\n            for main_version in main_versions.keys():\n                for component in main_versions[main_version]:\n                    logging.info(f\" {component.name}={main_version}\")\n\n            # generate new manifests\n            for release_version in sorted(main_versions.keys() - known_versions):\n                self.write_manifest(release_version, main_versions[release_version])\n                self.add_to_cron(release_version)\n\n    def create_manifest(self, version: str, components: List = []) -> InputManifest:\n        image_map = {\n            \"opensearch\": \"opensearchstaging/ci-runner:ci-runner-centos7-opensearch-build-v1\",\n            \"opensearch-dashboards\": \"opensearchstaging/ci-runner:ci-runner-centos7-opensearch-dashboards-build-v1\"\n        }\n\n        data: Dict = {\n            \"schema-version\": \"1.0\",\n            \"build\": {\n                \"name\": self.name,\n                \"version\": version\n            },\n            \"ci\": {\n                \"image\": {\n                    \"name\": image_map[self.prefix]\n                }\n            },\n            \"components\": [],\n        }\n\n        for component in components:\n            logging.info(f\" Adding {component.name}\")\n            data[\"components\"].append(component.to_dict())\n\n        return InputManifest(data)\n\n    def write_manifest(self, version: str, components: List = []) -> None:\n        logging.info(f\"Creating new version: {version}\")\n        manifest = self.create_manifest(version, components)\n        manifest_dir = os.path.join(self.manifests_path(), version)\n        os.makedirs(manifest_dir, exist_ok=True)\n        manifest_path = os.path.join(manifest_dir, f\"{self.prefix}-{version}.yml\")\n        manifest.to_file(manifest_path)\n        logging.info(f\"Wrote {manifest_path}\")\n\n    def add_to_cron(self, version: str) -> None:\n        logging.info(f\"Adding new version to cron: {version}\")\n        jenkinsfile = self.cron_jenkinsfile()\n        with open(jenkinsfile, \"r\") as f:\n            data = f.read()\n\n        cron_entry = f\"H/10 * * * * %INPUT_MANIFEST={version}/{self.prefix}-{version}.yml;TARGET_JOB_NAME=distribution-build-{self.prefix}\\n\"\n\n        if cron_entry in data:\n            raise ValueError(f\"{jenkinsfile} already contains an entry for {self.prefix} {version}\")\n\n        data = data.replace(\n            \"parameterizedCron '''\\n\",\n            f\"parameterizedCron '''\\n{' ' * 12}{cron_entry}\"\n        )\n\n        with open(jenkinsfile, \"w\") as f:\n            f.write(data)\n\n        logging.info(f\"Wrote {jenkinsfile}\")\n", "path": "src/manifests_workflow/input_manifests.py"}]}