We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Generated manifests for OpenSearch 2.x need custom JAVA_HOME
### Is your feature request related to a problem? Please describe

Automation opened a PR to generate the OpenSearch 3.0 manifest. That manifest was missing `JAVA_HOME` that was added manually in https://github.com/opensearch-project/opensearch-build/pull/2042.

### Describe the solution you'd like

Add a way to customize generated manifests for various products/versions, add `JAVA_HOME` to the OpenSearch ones.

### Describe alternatives you've considered

Keep doing it manually.

### Additional context

_No response_

--- 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/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         image_map = {
110             "opensearch": "opensearchstaging/ci-runner:ci-runner-centos7-opensearch-build-v2",
111             "opensearch-dashboards": "opensearchstaging/ci-runner:ci-runner-centos7-opensearch-dashboards-build-v2"
112         }
113 
114         data: Dict = {
115             "schema-version": "1.0",
116             "build": {
117                 "name": self.name,
118                 "version": version
119             },
120             "ci": {
121                 "image": {
122                     "name": image_map[self.prefix]
123                 }
124             },
125             "components": [],
126         }
127 
128         for component in components:
129             logging.info(f" Adding {component.name}")
130             data["components"].append(component.to_dict())
131 
132         return InputManifest(data)
133 
134     def write_manifest(self, version: str, components: List = []) -> None:
135         logging.info(f"Creating new version: {version}")
136         manifest = self.create_manifest(version, components)
137         manifest_dir = os.path.join(self.manifests_path(), version)
138         os.makedirs(manifest_dir, exist_ok=True)
139         manifest_path = os.path.join(manifest_dir, f"{self.prefix}-{version}.yml")
140         manifest.to_file(manifest_path)
141         logging.info(f"Wrote {manifest_path}")
142 
143     def add_to_cron(self, version: str) -> None:
144         logging.info(f"Adding new version to cron: {version}")
145         jenkinsfile = self.cron_jenkinsfile()
146         with open(jenkinsfile, "r") as f:
147             data = f.read()
148 
149         cron_entry = f"H 1 * * * %INPUT_MANIFEST={version}/{self.prefix}-{version}.yml;TARGET_JOB_NAME=distribution-build-{self.prefix}\n"
150 
151         if cron_entry in data:
152             raise ValueError(f"{jenkinsfile} already contains an entry for {self.prefix} {version}")
153 
154         data = data.replace(
155             "parameterizedCron '''\n",
156             f"parameterizedCron '''\n{' ' * 12}{cron_entry}"
157         )
158 
159         with open(jenkinsfile, "w") as f:
160             f.write(data)
161 
162         logging.info(f"Wrote {jenkinsfile}")
163
```
--- 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/input_manifests.py b/src/manifests_workflow/input_manifests.py
--- a/src/manifests_workflow/input_manifests.py
+++ b/src/manifests_workflow/input_manifests.py
@@ -11,7 +11,7 @@
 from abc import abstractmethod
 from typing import Dict, List, Type, Union
 
-from manifests.input_manifest import InputManifest
+from manifests.input_manifest import InputComponents, InputManifest
 from manifests.manifests import Manifests
 from manifests_workflow.component_opensearch import ComponentOpenSearch
 from manifests_workflow.component_opensearch_dashboards_min import ComponentOpenSearchDashboardsMin
@@ -48,7 +48,12 @@
         return results
 
     @abstractmethod
-    def update(self, min_klass: Union[Type[ComponentOpenSearchMin], Type[ComponentOpenSearchDashboardsMin]], component_klass: Type[ComponentOpenSearch], keep: bool = False) -> None:
+    def update(
+        self,
+        min_klass: Union[Type[ComponentOpenSearchMin], Type[ComponentOpenSearchDashboardsMin]],
+        component_klass: Type[ComponentOpenSearch],
+        keep: bool = False,
+    ) -> None:
         known_versions = self.versions
         logging.info(f"Known versions: {known_versions}")
         main_versions: Dict = {}
@@ -106,30 +111,23 @@
                 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-v2",
-            "opensearch-dashboards": "opensearchstaging/ci-runner:ci-runner-centos7-opensearch-dashboards-build-v2"
-        }
-
-        data: Dict = {
-            "schema-version": "1.0",
-            "build": {
-                "name": self.name,
-                "version": version
-            },
-            "ci": {
-                "image": {
-                    "name": image_map[self.prefix]
-                }
-            },
-            "components": [],
-        }
+        templates_base_path = os.path.join(self.manifests_path(), "templates")
+        template_version_folder = version.split(".")[0] + ".x"
+        template_full_path = os.path.join(templates_base_path, self.prefix, template_version_folder, "manifest.yml")
+        if not os.path.exists(template_full_path):
+            template_full_path = os.path.join(templates_base_path, self.prefix, "default", "manifest.yml")
+
+        manifest = InputManifest.from_file(open(template_full_path))
+
+        manifest.build.version = version
+        manifests_components = []
 
         for component in components:
             logging.info(f" Adding {component.name}")
-            data["components"].append(component.to_dict())
+            manifests_components.append(component.to_dict())
 
-        return InputManifest(data)
+        manifest.components = InputComponents(manifests_components)  # type: ignore
+        return manifest
 
     def write_manifest(self, version: str, components: List = []) -> None:
         logging.info(f"Creating new version: {version}")
@@ -151,10 +149,7 @@
         if cron_entry in data:
             raise ValueError(f"{jenkinsfile} already contains an entry for {self.prefix} {version}")
 
-        data = data.replace(
-            "parameterizedCron '''\n",
-            f"parameterizedCron '''\n{' ' * 12}{cron_entry}"
-        )
+        data = data.replace("parameterizedCron '''\n", f"parameterizedCron '''\n{' ' * 12}{cron_entry}")
 
         with open(jenkinsfile, "w") as f:
             f.write(data)

{"golden_diff": "diff --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@@ -11,7 +11,7 @@\n from abc import abstractmethod\n from typing import Dict, List, Type, Union\n \n-from manifests.input_manifest import InputManifest\n+from manifests.input_manifest import InputComponents, InputManifest\n from manifests.manifests import Manifests\n from manifests_workflow.component_opensearch import ComponentOpenSearch\n from manifests_workflow.component_opensearch_dashboards_min import ComponentOpenSearchDashboardsMin\n@@ -48,7 +48,12 @@\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+    def update(\n+        self,\n+        min_klass: Union[Type[ComponentOpenSearchMin], Type[ComponentOpenSearchDashboardsMin]],\n+        component_klass: Type[ComponentOpenSearch],\n+        keep: bool = False,\n+    ) -> None:\n         known_versions = self.versions\n         logging.info(f\"Known versions: {known_versions}\")\n         main_versions: Dict = {}\n@@ -106,30 +111,23 @@\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-v2\",\n-            \"opensearch-dashboards\": \"opensearchstaging/ci-runner:ci-runner-centos7-opensearch-dashboards-build-v2\"\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+        templates_base_path = os.path.join(self.manifests_path(), \"templates\")\n+        template_version_folder = version.split(\".\")[0] + \".x\"\n+        template_full_path = os.path.join(templates_base_path, self.prefix, template_version_folder, \"manifest.yml\")\n+        if not os.path.exists(template_full_path):\n+            template_full_path = os.path.join(templates_base_path, self.prefix, \"default\", \"manifest.yml\")\n+\n+        manifest = InputManifest.from_file(open(template_full_path))\n+\n+        manifest.build.version = version\n+        manifests_components = []\n \n         for component in components:\n             logging.info(f\" Adding {component.name}\")\n-            data[\"components\"].append(component.to_dict())\n+            manifests_components.append(component.to_dict())\n \n-        return InputManifest(data)\n+        manifest.components = InputComponents(manifests_components)  # type: ignore\n+        return manifest\n \n     def write_manifest(self, version: str, components: List = []) -> None:\n         logging.info(f\"Creating new version: {version}\")\n@@ -151,10 +149,7 @@\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+        data = data.replace(\"parameterizedCron '''\\n\", f\"parameterizedCron '''\\n{' ' * 12}{cron_entry}\")\n \n         with open(jenkinsfile, \"w\") as f:\n             f.write(data)\n", "issue": "Generated manifests for OpenSearch 2.x need custom JAVA_HOME\n### Is your feature request related to a problem? Please describe\n\nAutomation opened a PR to generate the OpenSearch 3.0 manifest. That manifest was missing `JAVA_HOME` that was added manually in https://github.com/opensearch-project/opensearch-build/pull/2042.\n\n### Describe the solution you'd like\n\nAdd a way to customize generated manifests for various products/versions, add `JAVA_HOME` to the OpenSearch ones.\n\n### Describe alternatives you've considered\n\nKeep doing it manually.\n\n### Additional context\n\n_No response_\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 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-v2\",\n            \"opensearch-dashboards\": \"opensearchstaging/ci-runner:ci-runner-centos7-opensearch-dashboards-build-v2\"\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 1 * * * %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\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 InputComponents, 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(\n        self,\n        min_klass: Union[Type[ComponentOpenSearchMin], Type[ComponentOpenSearchDashboardsMin]],\n        component_klass: Type[ComponentOpenSearch],\n        keep: bool = False,\n    ) -> 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        templates_base_path = os.path.join(self.manifests_path(), \"templates\")\n        template_version_folder = version.split(\".\")[0] + \".x\"\n        template_full_path = os.path.join(templates_base_path, self.prefix, template_version_folder, \"manifest.yml\")\n        if not os.path.exists(template_full_path):\n            template_full_path = os.path.join(templates_base_path, self.prefix, \"default\", \"manifest.yml\")\n\n        manifest = InputManifest.from_file(open(template_full_path))\n\n        manifest.build.version = version\n        manifests_components = []\n\n        for component in components:\n            logging.info(f\" Adding {component.name}\")\n            manifests_components.append(component.to_dict())\n\n        manifest.components = InputComponents(manifests_components)  # type: ignore\n        return manifest\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 1 * * * %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(\"parameterizedCron '''\\n\", f\"parameterizedCron '''\\n{' ' * 12}{cron_entry}\")\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"}]}