We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[Bug]: Distribution builds can have path collisions
Distribution builds are not namespace'd based on the type of artifact produced so an OpenSearch and OpenSearch Dashboard build could collide.

### Actual
The current path generated is `https://ci.opensearch.org/ci/dbc/builds/1.1.0/328/x64/manifest.yml`.

### Expected
It should have the distribution type in the path like `https://ci.opensearch.org/ci/dbc/opensearch/builds/1.1.0/328/x64/manifest.yml` and `https://ci.opensearch.org/ci/dbc/opensearch-dashboards/builds/1.1.0/328/x64/manifest.yml`

### Required changes
- [x] Update the build jobs to include the build type in the url for the generated manifests, see [bundle_recorder.py](https://github.com/opensearch-project/opensearch-build/blob/main/src/assemble_workflow/bundle_recorder.py).
- [x] Update the jenkinsfile to upload to a destination based on the build type, see [Jenkinsfile](https://github.com/opensearch-project/opensearch-build/blob/main/Jenkinsfile#L127).

### Other changes
- [ ] Migrate/ update existing artifacts to the new destination
- [x] Update the [roles](https://github.com/opensearch-project/opensearch-build/blob/main/deployment/lib/identities.ts#L11) to be separate between jobs to prevent any possible conflicts, then update the jenkins jobs to build to the correct destination.
- [x] https://github.com/opensearch-project/opensearch-build/issues/661
- [x] https://github.com/opensearch-project/opensearch-build/issues/714


--- 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/run_assemble.py`
Content:
```
1 #!/usr/bin/env python
2 
3 # SPDX-License-Identifier: Apache-2.0
4 #
5 # The OpenSearch Contributors require contributions made to
6 # this file be licensed under the Apache-2.0 license or a
7 # compatible open source license.
8 
9 import argparse
10 import logging
11 import os
12 import sys
13 
14 from assemble_workflow.bundle_recorder import BundleRecorder
15 from assemble_workflow.bundles import Bundles
16 from manifests.build_manifest import BuildManifest
17 from system import console
18 from system.temporary_directory import TemporaryDirectory
19 
20 
21 def main():
22     parser = argparse.ArgumentParser(description="Assemble an OpenSearch Bundle")
23     parser.add_argument("manifest", type=argparse.FileType("r"), help="Manifest file.")
24     parser.add_argument(
25         "-v",
26         "--verbose",
27         help="Show more verbose output.",
28         action="store_const",
29         default=logging.INFO,
30         const=logging.DEBUG,
31         dest="logging_level",
32     )
33     args = parser.parse_args()
34 
35     console.configure(level=args.logging_level)
36 
37     build_manifest = BuildManifest.from_file(args.manifest)
38     build = build_manifest.build
39     artifacts_dir = os.path.dirname(os.path.realpath(args.manifest.name))
40     output_dir = os.path.join(os.getcwd(), "bundle")
41     os.makedirs(output_dir, exist_ok=True)
42 
43     with TemporaryDirectory() as work_dir:
44         logging.info(f"Bundling {build.name} ({build.architecture}) on {build.platform} into {output_dir} ...")
45 
46         os.chdir(work_dir.name)
47 
48         bundle_recorder = BundleRecorder(build, output_dir, artifacts_dir)
49 
50         bundle = Bundles.create(build_manifest, artifacts_dir, bundle_recorder)
51 
52         bundle.install_min()
53         bundle.install_plugins()
54         logging.info(f"Installed plugins: {bundle.installed_plugins}")
55 
56         #  Save a copy of the manifest inside of the tar
57         bundle_recorder.write_manifest(bundle.archive_path)
58         bundle.build_tar(output_dir)
59 
60         bundle_recorder.write_manifest(output_dir)
61 
62     logging.info("Done.")
63 
64 
65 if __name__ == "__main__":
66     sys.exit(main())
67
```

Path: `src/assemble_workflow/bundle_recorder.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 os
8 from urllib.parse import urljoin
9 
10 from manifests.bundle_manifest import BundleManifest
11 
12 
13 class BundleRecorder:
14     def __init__(self, build, output_dir, artifacts_dir):
15         self.output_dir = output_dir
16         self.build_id = build.id
17         self.public_url = os.getenv("PUBLIC_ARTIFACT_URL", None)
18         self.version = build.version
19         self.tar_name = self.__get_tar_name(build)
20         self.artifacts_dir = artifacts_dir
21         self.architecture = build.architecture
22         self.bundle_manifest = self.BundleManifestBuilder(
23             build.id,
24             build.name,
25             build.version,
26             build.platform,
27             build.architecture,
28             self.__get_tar_location(),
29         )
30 
31     def __get_tar_name(self, build):
32         parts = [
33             build.name.lower().replace(" ", "-"),
34             build.version,
35             build.platform,
36             build.architecture,
37         ]
38         return "-".join(parts) + ".tar.gz"
39 
40     def __get_public_url_path(self, folder, rel_path):
41         path = "/".join((folder, self.version, self.build_id, self.architecture, rel_path))
42         return urljoin(self.public_url + "/", path)
43 
44     def __get_location(self, folder_name, rel_path, abs_path):
45         if self.public_url:
46             return self.__get_public_url_path(folder_name, rel_path)
47         return abs_path
48 
49     # Assembled bundles are expected to be served from a separate "bundles" folder
50     # Example: https://artifacts.opensearch.org/bundles/1.0.0/<build-id
51     def __get_tar_location(self):
52         return self.__get_location("bundles", self.tar_name, os.path.join(self.output_dir, self.tar_name))
53 
54     # Build artifacts are expected to be served from a "builds" folder
55     # Example: https://artifacts.opensearch.org/builds/1.0.0/<build-id>
56     def __get_component_location(self, component_rel_path):
57         abs_path = os.path.join(self.artifacts_dir, component_rel_path)
58         return self.__get_location("builds", component_rel_path, abs_path)
59 
60     def record_component(self, component, rel_path):
61         self.bundle_manifest.append_component(
62             component.name,
63             component.repository,
64             component.ref,
65             component.commit_id,
66             self.__get_component_location(rel_path),
67         )
68 
69     def get_manifest(self):
70         return self.bundle_manifest.to_manifest()
71 
72     def write_manifest(self, folder):
73         manifest_path = os.path.join(folder, "manifest.yml")
74         self.get_manifest().to_file(manifest_path)
75 
76     class BundleManifestBuilder:
77         def __init__(self, build_id, name, version, platform, architecture, location):
78             self.data = {}
79             self.data["build"] = {}
80             self.data["build"]["id"] = build_id
81             self.data["build"]["name"] = name
82             self.data["build"]["version"] = str(version)
83             self.data["build"]["platform"] = platform
84             self.data["build"]["architecture"] = architecture
85             self.data["build"]["location"] = location
86             self.data["schema-version"] = "1.1"
87             # We need to store components as a hash so that we can append artifacts by component name
88             # When we convert to a BundleManifest this will get converted back into a list
89             self.data["components"] = []
90 
91         def append_component(self, name, repository_url, ref, commit_id, location):
92             component = {
93                 "name": name,
94                 "repository": repository_url,
95                 "ref": ref,
96                 "commit_id": commit_id,
97                 "location": location,
98             }
99             self.data["components"].append(component)
100 
101         def to_manifest(self):
102             return BundleManifest(self.data)
103
```

Path: `src/run_build.py`
Content:
```
1 #!/usr/bin/env python
2 
3 # SPDX-License-Identifier: Apache-2.0
4 #
5 # The OpenSearch Contributors require contributions made to
6 # this file be licensed under the Apache-2.0 license or a
7 # compatible open source license.
8 
9 import logging
10 import os
11 import sys
12 
13 from build_workflow.build_args import BuildArgs
14 from build_workflow.build_recorder import BuildRecorder
15 from build_workflow.build_target import BuildTarget
16 from build_workflow.builder import Builder
17 from git.git_repository import GitRepository
18 from manifests.input_manifest import InputManifest
19 from system import console
20 from system.temporary_directory import TemporaryDirectory
21 
22 
23 def main():
24     args = BuildArgs()
25     console.configure(level=args.logging_level)
26     manifest = InputManifest.from_file(args.manifest)
27 
28     with TemporaryDirectory(keep=args.keep) as work_dir:
29         output_dir = os.path.join(os.getcwd(), "artifacts")
30 
31         logging.info(f"Building in {work_dir.name}")
32 
33         os.chdir(work_dir.name)
34 
35         target = BuildTarget(
36             name=manifest.build.name,
37             version=manifest.build.version,
38             snapshot=args.snapshot,
39             output_dir=output_dir,
40             platform=args.platform,
41             architecture=args.architecture,
42         )
43 
44         os.makedirs(target.output_dir, exist_ok=True)
45 
46         build_recorder = BuildRecorder(target)
47 
48         logging.info(
49             f"Building {manifest.build.name} ({target.architecture}) into {target.output_dir}"
50         )
51 
52         for component in manifest.components:
53 
54             if args.component and args.component != component.name:
55                 logging.info(f"Skipping {component.name}")
56                 continue
57 
58             logging.info(f"Building {component.name}")
59 
60             with GitRepository(
61                 component.repository,
62                 component.ref,
63                 os.path.join(work_dir.name, component.name),
64                 component.working_directory,
65             ) as repo:
66                 try:
67                     builder = Builder(component.name, repo, build_recorder)
68                     builder.build(target)
69                     builder.export_artifacts()
70                 except:
71                     logging.error(f"Error building {component.name}, retry with: {args.component_command(component.name)}")
72                     raise
73 
74         build_recorder.write_manifest()
75 
76     logging.info("Done.")
77 
78 
79 if __name__ == "__main__":
80     sys.exit(main())
81
```
--- 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/bundle_recorder.py b/src/assemble_workflow/bundle_recorder.py
--- a/src/assemble_workflow/bundle_recorder.py
+++ b/src/assemble_workflow/bundle_recorder.py
@@ -11,10 +11,10 @@
 
 
 class BundleRecorder:
-    def __init__(self, build, output_dir, artifacts_dir):
+    def __init__(self, build, output_dir, artifacts_dir, base_url):
         self.output_dir = output_dir
         self.build_id = build.id
-        self.public_url = os.getenv("PUBLIC_ARTIFACT_URL", None)
+        self.base_url = base_url
         self.version = build.version
         self.tar_name = self.__get_tar_name(build)
         self.artifacts_dir = artifacts_dir
@@ -38,18 +38,18 @@
         return "-".join(parts) + ".tar.gz"
 
     def __get_public_url_path(self, folder, rel_path):
-        path = "/".join((folder, self.version, self.build_id, self.architecture, rel_path))
-        return urljoin(self.public_url + "/", path)
+        path = "/".join((folder, rel_path))
+        return urljoin(self.base_url + "/", path)
 
     def __get_location(self, folder_name, rel_path, abs_path):
-        if self.public_url:
+        if self.base_url:
             return self.__get_public_url_path(folder_name, rel_path)
         return abs_path
 
     # Assembled bundles are expected to be served from a separate "bundles" folder
     # Example: https://artifacts.opensearch.org/bundles/1.0.0/<build-id
     def __get_tar_location(self):
-        return self.__get_location("bundles", self.tar_name, os.path.join(self.output_dir, self.tar_name))
+        return self.__get_location("dist", self.tar_name, os.path.join(self.output_dir, self.tar_name))
 
     # Build artifacts are expected to be served from a "builds" folder
     # Example: https://artifacts.opensearch.org/builds/1.0.0/<build-id>
diff --git a/src/run_assemble.py b/src/run_assemble.py
--- a/src/run_assemble.py
+++ b/src/run_assemble.py
@@ -30,6 +30,7 @@
         const=logging.DEBUG,
         dest="logging_level",
     )
+    parser.add_argument("-b", "--base-url", dest='base_url', help="The base url to download the artifacts.")
     args = parser.parse_args()
 
     console.configure(level=args.logging_level)
@@ -37,7 +38,7 @@
     build_manifest = BuildManifest.from_file(args.manifest)
     build = build_manifest.build
     artifacts_dir = os.path.dirname(os.path.realpath(args.manifest.name))
-    output_dir = os.path.join(os.getcwd(), "bundle")
+    output_dir = os.path.join(os.getcwd(), "dist")
     os.makedirs(output_dir, exist_ok=True)
 
     with TemporaryDirectory() as work_dir:
@@ -45,7 +46,7 @@
 
         os.chdir(work_dir.name)
 
-        bundle_recorder = BundleRecorder(build, output_dir, artifacts_dir)
+        bundle_recorder = BundleRecorder(build, output_dir, artifacts_dir, args.base_url)
 
         bundle = Bundles.create(build_manifest, artifacts_dir, bundle_recorder)
 
diff --git a/src/run_build.py b/src/run_build.py
--- a/src/run_build.py
+++ b/src/run_build.py
@@ -26,7 +26,7 @@
     manifest = InputManifest.from_file(args.manifest)
 
     with TemporaryDirectory(keep=args.keep) as work_dir:
-        output_dir = os.path.join(os.getcwd(), "artifacts")
+        output_dir = os.path.join(os.getcwd(), "builds")
 
         logging.info(f"Building in {work_dir.name}")

{"golden_diff": "diff --git a/src/assemble_workflow/bundle_recorder.py b/src/assemble_workflow/bundle_recorder.py\n--- a/src/assemble_workflow/bundle_recorder.py\n+++ b/src/assemble_workflow/bundle_recorder.py\n@@ -11,10 +11,10 @@\n \n \n class BundleRecorder:\n-    def __init__(self, build, output_dir, artifacts_dir):\n+    def __init__(self, build, output_dir, artifacts_dir, base_url):\n         self.output_dir = output_dir\n         self.build_id = build.id\n-        self.public_url = os.getenv(\"PUBLIC_ARTIFACT_URL\", None)\n+        self.base_url = base_url\n         self.version = build.version\n         self.tar_name = self.__get_tar_name(build)\n         self.artifacts_dir = artifacts_dir\n@@ -38,18 +38,18 @@\n         return \"-\".join(parts) + \".tar.gz\"\n \n     def __get_public_url_path(self, folder, rel_path):\n-        path = \"/\".join((folder, self.version, self.build_id, self.architecture, rel_path))\n-        return urljoin(self.public_url + \"/\", path)\n+        path = \"/\".join((folder, rel_path))\n+        return urljoin(self.base_url + \"/\", path)\n \n     def __get_location(self, folder_name, rel_path, abs_path):\n-        if self.public_url:\n+        if self.base_url:\n             return self.__get_public_url_path(folder_name, rel_path)\n         return abs_path\n \n     # Assembled bundles are expected to be served from a separate \"bundles\" folder\n     # Example: https://artifacts.opensearch.org/bundles/1.0.0/<build-id\n     def __get_tar_location(self):\n-        return self.__get_location(\"bundles\", self.tar_name, os.path.join(self.output_dir, self.tar_name))\n+        return self.__get_location(\"dist\", self.tar_name, os.path.join(self.output_dir, self.tar_name))\n \n     # Build artifacts are expected to be served from a \"builds\" folder\n     # Example: https://artifacts.opensearch.org/builds/1.0.0/<build-id>\ndiff --git a/src/run_assemble.py b/src/run_assemble.py\n--- a/src/run_assemble.py\n+++ b/src/run_assemble.py\n@@ -30,6 +30,7 @@\n         const=logging.DEBUG,\n         dest=\"logging_level\",\n     )\n+    parser.add_argument(\"-b\", \"--base-url\", dest='base_url', help=\"The base url to download the artifacts.\")\n     args = parser.parse_args()\n \n     console.configure(level=args.logging_level)\n@@ -37,7 +38,7 @@\n     build_manifest = BuildManifest.from_file(args.manifest)\n     build = build_manifest.build\n     artifacts_dir = os.path.dirname(os.path.realpath(args.manifest.name))\n-    output_dir = os.path.join(os.getcwd(), \"bundle\")\n+    output_dir = os.path.join(os.getcwd(), \"dist\")\n     os.makedirs(output_dir, exist_ok=True)\n \n     with TemporaryDirectory() as work_dir:\n@@ -45,7 +46,7 @@\n \n         os.chdir(work_dir.name)\n \n-        bundle_recorder = BundleRecorder(build, output_dir, artifacts_dir)\n+        bundle_recorder = BundleRecorder(build, output_dir, artifacts_dir, args.base_url)\n \n         bundle = Bundles.create(build_manifest, artifacts_dir, bundle_recorder)\n \ndiff --git a/src/run_build.py b/src/run_build.py\n--- a/src/run_build.py\n+++ b/src/run_build.py\n@@ -26,7 +26,7 @@\n     manifest = InputManifest.from_file(args.manifest)\n \n     with TemporaryDirectory(keep=args.keep) as work_dir:\n-        output_dir = os.path.join(os.getcwd(), \"artifacts\")\n+        output_dir = os.path.join(os.getcwd(), \"builds\")\n \n         logging.info(f\"Building in {work_dir.name}\")\n", "issue": "[Bug]: Distribution builds can have path collisions\nDistribution builds are not namespace'd based on the type of artifact produced so an OpenSearch and OpenSearch Dashboard build could collide.\r\n\r\n### Actual\r\nThe current path generated is `https://ci.opensearch.org/ci/dbc/builds/1.1.0/328/x64/manifest.yml`.\r\n\r\n### Expected\r\nIt should have the distribution type in the path like `https://ci.opensearch.org/ci/dbc/opensearch/builds/1.1.0/328/x64/manifest.yml` and `https://ci.opensearch.org/ci/dbc/opensearch-dashboards/builds/1.1.0/328/x64/manifest.yml`\r\n\r\n### Required changes\r\n- [x] Update the build jobs to include the build type in the url for the generated manifests, see [bundle_recorder.py](https://github.com/opensearch-project/opensearch-build/blob/main/src/assemble_workflow/bundle_recorder.py).\r\n- [x] Update the jenkinsfile to upload to a destination based on the build type, see [Jenkinsfile](https://github.com/opensearch-project/opensearch-build/blob/main/Jenkinsfile#L127).\r\n\r\n### Other changes\r\n- [ ] Migrate/ update existing artifacts to the new destination\r\n- [x] Update the [roles](https://github.com/opensearch-project/opensearch-build/blob/main/deployment/lib/identities.ts#L11) to be separate between jobs to prevent any possible conflicts, then update the jenkins jobs to build to the correct destination.\r\n- [x] https://github.com/opensearch-project/opensearch-build/issues/661\r\n- [x] https://github.com/opensearch-project/opensearch-build/issues/714\r\n\n", "before_files": [{"content": "#!/usr/bin/env python\n\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 argparse\nimport logging\nimport os\nimport sys\n\nfrom assemble_workflow.bundle_recorder import BundleRecorder\nfrom assemble_workflow.bundles import Bundles\nfrom manifests.build_manifest import BuildManifest\nfrom system import console\nfrom system.temporary_directory import TemporaryDirectory\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Assemble an OpenSearch Bundle\")\n    parser.add_argument(\"manifest\", type=argparse.FileType(\"r\"), help=\"Manifest file.\")\n    parser.add_argument(\n        \"-v\",\n        \"--verbose\",\n        help=\"Show more verbose output.\",\n        action=\"store_const\",\n        default=logging.INFO,\n        const=logging.DEBUG,\n        dest=\"logging_level\",\n    )\n    args = parser.parse_args()\n\n    console.configure(level=args.logging_level)\n\n    build_manifest = BuildManifest.from_file(args.manifest)\n    build = build_manifest.build\n    artifacts_dir = os.path.dirname(os.path.realpath(args.manifest.name))\n    output_dir = os.path.join(os.getcwd(), \"bundle\")\n    os.makedirs(output_dir, exist_ok=True)\n\n    with TemporaryDirectory() as work_dir:\n        logging.info(f\"Bundling {build.name} ({build.architecture}) on {build.platform} into {output_dir} ...\")\n\n        os.chdir(work_dir.name)\n\n        bundle_recorder = BundleRecorder(build, output_dir, artifacts_dir)\n\n        bundle = Bundles.create(build_manifest, artifacts_dir, bundle_recorder)\n\n        bundle.install_min()\n        bundle.install_plugins()\n        logging.info(f\"Installed plugins: {bundle.installed_plugins}\")\n\n        #  Save a copy of the manifest inside of the tar\n        bundle_recorder.write_manifest(bundle.archive_path)\n        bundle.build_tar(output_dir)\n\n        bundle_recorder.write_manifest(output_dir)\n\n    logging.info(\"Done.\")\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n", "path": "src/run_assemble.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 os\nfrom urllib.parse import urljoin\n\nfrom manifests.bundle_manifest import BundleManifest\n\n\nclass BundleRecorder:\n    def __init__(self, build, output_dir, artifacts_dir):\n        self.output_dir = output_dir\n        self.build_id = build.id\n        self.public_url = os.getenv(\"PUBLIC_ARTIFACT_URL\", None)\n        self.version = build.version\n        self.tar_name = self.__get_tar_name(build)\n        self.artifacts_dir = artifacts_dir\n        self.architecture = build.architecture\n        self.bundle_manifest = self.BundleManifestBuilder(\n            build.id,\n            build.name,\n            build.version,\n            build.platform,\n            build.architecture,\n            self.__get_tar_location(),\n        )\n\n    def __get_tar_name(self, build):\n        parts = [\n            build.name.lower().replace(\" \", \"-\"),\n            build.version,\n            build.platform,\n            build.architecture,\n        ]\n        return \"-\".join(parts) + \".tar.gz\"\n\n    def __get_public_url_path(self, folder, rel_path):\n        path = \"/\".join((folder, self.version, self.build_id, self.architecture, rel_path))\n        return urljoin(self.public_url + \"/\", path)\n\n    def __get_location(self, folder_name, rel_path, abs_path):\n        if self.public_url:\n            return self.__get_public_url_path(folder_name, rel_path)\n        return abs_path\n\n    # Assembled bundles are expected to be served from a separate \"bundles\" folder\n    # Example: https://artifacts.opensearch.org/bundles/1.0.0/<build-id\n    def __get_tar_location(self):\n        return self.__get_location(\"bundles\", self.tar_name, os.path.join(self.output_dir, self.tar_name))\n\n    # Build artifacts are expected to be served from a \"builds\" folder\n    # Example: https://artifacts.opensearch.org/builds/1.0.0/<build-id>\n    def __get_component_location(self, component_rel_path):\n        abs_path = os.path.join(self.artifacts_dir, component_rel_path)\n        return self.__get_location(\"builds\", component_rel_path, abs_path)\n\n    def record_component(self, component, rel_path):\n        self.bundle_manifest.append_component(\n            component.name,\n            component.repository,\n            component.ref,\n            component.commit_id,\n            self.__get_component_location(rel_path),\n        )\n\n    def get_manifest(self):\n        return self.bundle_manifest.to_manifest()\n\n    def write_manifest(self, folder):\n        manifest_path = os.path.join(folder, \"manifest.yml\")\n        self.get_manifest().to_file(manifest_path)\n\n    class BundleManifestBuilder:\n        def __init__(self, build_id, name, version, platform, architecture, location):\n            self.data = {}\n            self.data[\"build\"] = {}\n            self.data[\"build\"][\"id\"] = build_id\n            self.data[\"build\"][\"name\"] = name\n            self.data[\"build\"][\"version\"] = str(version)\n            self.data[\"build\"][\"platform\"] = platform\n            self.data[\"build\"][\"architecture\"] = architecture\n            self.data[\"build\"][\"location\"] = location\n            self.data[\"schema-version\"] = \"1.1\"\n            # We need to store components as a hash so that we can append artifacts by component name\n            # When we convert to a BundleManifest this will get converted back into a list\n            self.data[\"components\"] = []\n\n        def append_component(self, name, repository_url, ref, commit_id, location):\n            component = {\n                \"name\": name,\n                \"repository\": repository_url,\n                \"ref\": ref,\n                \"commit_id\": commit_id,\n                \"location\": location,\n            }\n            self.data[\"components\"].append(component)\n\n        def to_manifest(self):\n            return BundleManifest(self.data)\n", "path": "src/assemble_workflow/bundle_recorder.py"}, {"content": "#!/usr/bin/env python\n\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 logging\nimport os\nimport sys\n\nfrom build_workflow.build_args import BuildArgs\nfrom build_workflow.build_recorder import BuildRecorder\nfrom build_workflow.build_target import BuildTarget\nfrom build_workflow.builder import Builder\nfrom git.git_repository import GitRepository\nfrom manifests.input_manifest import InputManifest\nfrom system import console\nfrom system.temporary_directory import TemporaryDirectory\n\n\ndef main():\n    args = BuildArgs()\n    console.configure(level=args.logging_level)\n    manifest = InputManifest.from_file(args.manifest)\n\n    with TemporaryDirectory(keep=args.keep) as work_dir:\n        output_dir = os.path.join(os.getcwd(), \"artifacts\")\n\n        logging.info(f\"Building in {work_dir.name}\")\n\n        os.chdir(work_dir.name)\n\n        target = BuildTarget(\n            name=manifest.build.name,\n            version=manifest.build.version,\n            snapshot=args.snapshot,\n            output_dir=output_dir,\n            platform=args.platform,\n            architecture=args.architecture,\n        )\n\n        os.makedirs(target.output_dir, exist_ok=True)\n\n        build_recorder = BuildRecorder(target)\n\n        logging.info(\n            f\"Building {manifest.build.name} ({target.architecture}) into {target.output_dir}\"\n        )\n\n        for component in manifest.components:\n\n            if args.component and args.component != component.name:\n                logging.info(f\"Skipping {component.name}\")\n                continue\n\n            logging.info(f\"Building {component.name}\")\n\n            with GitRepository(\n                component.repository,\n                component.ref,\n                os.path.join(work_dir.name, component.name),\n                component.working_directory,\n            ) as repo:\n                try:\n                    builder = Builder(component.name, repo, build_recorder)\n                    builder.build(target)\n                    builder.export_artifacts()\n                except:\n                    logging.error(f\"Error building {component.name}, retry with: {args.component_command(component.name)}\")\n                    raise\n\n        build_recorder.write_manifest()\n\n    logging.info(\"Done.\")\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n", "path": "src/run_build.py"}], "after_files": [{"content": "#!/usr/bin/env python\n\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 argparse\nimport logging\nimport os\nimport sys\n\nfrom assemble_workflow.bundle_recorder import BundleRecorder\nfrom assemble_workflow.bundles import Bundles\nfrom manifests.build_manifest import BuildManifest\nfrom system import console\nfrom system.temporary_directory import TemporaryDirectory\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Assemble an OpenSearch Bundle\")\n    parser.add_argument(\"manifest\", type=argparse.FileType(\"r\"), help=\"Manifest file.\")\n    parser.add_argument(\n        \"-v\",\n        \"--verbose\",\n        help=\"Show more verbose output.\",\n        action=\"store_const\",\n        default=logging.INFO,\n        const=logging.DEBUG,\n        dest=\"logging_level\",\n    )\n    parser.add_argument(\"-b\", \"--base-url\", dest='base_url', help=\"The base url to download the artifacts.\")\n    args = parser.parse_args()\n\n    console.configure(level=args.logging_level)\n\n    build_manifest = BuildManifest.from_file(args.manifest)\n    build = build_manifest.build\n    artifacts_dir = os.path.dirname(os.path.realpath(args.manifest.name))\n    output_dir = os.path.join(os.getcwd(), \"dist\")\n    os.makedirs(output_dir, exist_ok=True)\n\n    with TemporaryDirectory() as work_dir:\n        logging.info(f\"Bundling {build.name} ({build.architecture}) on {build.platform} into {output_dir} ...\")\n\n        os.chdir(work_dir.name)\n\n        bundle_recorder = BundleRecorder(build, output_dir, artifacts_dir, args.base_url)\n\n        bundle = Bundles.create(build_manifest, artifacts_dir, bundle_recorder)\n\n        bundle.install_min()\n        bundle.install_plugins()\n        logging.info(f\"Installed plugins: {bundle.installed_plugins}\")\n\n        #  Save a copy of the manifest inside of the tar\n        bundle_recorder.write_manifest(bundle.archive_path)\n        bundle.build_tar(output_dir)\n\n        bundle_recorder.write_manifest(output_dir)\n\n    logging.info(\"Done.\")\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n", "path": "src/run_assemble.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 os\nfrom urllib.parse import urljoin\n\nfrom manifests.bundle_manifest import BundleManifest\n\n\nclass BundleRecorder:\n    def __init__(self, build, output_dir, artifacts_dir, base_url):\n        self.output_dir = output_dir\n        self.build_id = build.id\n        self.base_url = base_url\n        self.version = build.version\n        self.tar_name = self.__get_tar_name(build)\n        self.artifacts_dir = artifacts_dir\n        self.architecture = build.architecture\n        self.bundle_manifest = self.BundleManifestBuilder(\n            build.id,\n            build.name,\n            build.version,\n            build.platform,\n            build.architecture,\n            self.__get_tar_location(),\n        )\n\n    def __get_tar_name(self, build):\n        parts = [\n            build.name.lower().replace(\" \", \"-\"),\n            build.version,\n            build.platform,\n            build.architecture,\n        ]\n        return \"-\".join(parts) + \".tar.gz\"\n\n    def __get_public_url_path(self, folder, rel_path):\n        path = \"/\".join((folder, rel_path))\n        return urljoin(self.base_url + \"/\", path)\n\n    def __get_location(self, folder_name, rel_path, abs_path):\n        if self.base_url:\n            return self.__get_public_url_path(folder_name, rel_path)\n        return abs_path\n\n    # Assembled bundles are expected to be served from a separate \"bundles\" folder\n    # Example: https://artifacts.opensearch.org/bundles/1.0.0/<build-id\n    def __get_tar_location(self):\n        return self.__get_location(\"dist\", self.tar_name, os.path.join(self.output_dir, self.tar_name))\n\n    # Build artifacts are expected to be served from a \"builds\" folder\n    # Example: https://artifacts.opensearch.org/builds/1.0.0/<build-id>\n    def __get_component_location(self, component_rel_path):\n        abs_path = os.path.join(self.artifacts_dir, component_rel_path)\n        return self.__get_location(\"builds\", component_rel_path, abs_path)\n\n    def record_component(self, component, rel_path):\n        self.bundle_manifest.append_component(\n            component.name,\n            component.repository,\n            component.ref,\n            component.commit_id,\n            self.__get_component_location(rel_path),\n        )\n\n    def get_manifest(self):\n        return self.bundle_manifest.to_manifest()\n\n    def write_manifest(self, folder):\n        manifest_path = os.path.join(folder, \"manifest.yml\")\n        self.get_manifest().to_file(manifest_path)\n\n    class BundleManifestBuilder:\n        def __init__(self, build_id, name, version, platform, architecture, location):\n            self.data = {}\n            self.data[\"build\"] = {}\n            self.data[\"build\"][\"id\"] = build_id\n            self.data[\"build\"][\"name\"] = name\n            self.data[\"build\"][\"version\"] = str(version)\n            self.data[\"build\"][\"platform\"] = platform\n            self.data[\"build\"][\"architecture\"] = architecture\n            self.data[\"build\"][\"location\"] = location\n            self.data[\"schema-version\"] = \"1.1\"\n            # We need to store components as a hash so that we can append artifacts by component name\n            # When we convert to a BundleManifest this will get converted back into a list\n            self.data[\"components\"] = []\n\n        def append_component(self, name, repository_url, ref, commit_id, location):\n            component = {\n                \"name\": name,\n                \"repository\": repository_url,\n                \"ref\": ref,\n                \"commit_id\": commit_id,\n                \"location\": location,\n            }\n            self.data[\"components\"].append(component)\n\n        def to_manifest(self):\n            return BundleManifest(self.data)\n", "path": "src/assemble_workflow/bundle_recorder.py"}, {"content": "#!/usr/bin/env python\n\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 logging\nimport os\nimport sys\n\nfrom build_workflow.build_args import BuildArgs\nfrom build_workflow.build_recorder import BuildRecorder\nfrom build_workflow.build_target import BuildTarget\nfrom build_workflow.builder import Builder\nfrom git.git_repository import GitRepository\nfrom manifests.input_manifest import InputManifest\nfrom system import console\nfrom system.temporary_directory import TemporaryDirectory\n\n\ndef main():\n    args = BuildArgs()\n    console.configure(level=args.logging_level)\n    manifest = InputManifest.from_file(args.manifest)\n\n    with TemporaryDirectory(keep=args.keep) as work_dir:\n        output_dir = os.path.join(os.getcwd(), \"builds\")\n\n        logging.info(f\"Building in {work_dir.name}\")\n\n        os.chdir(work_dir.name)\n\n        target = BuildTarget(\n            name=manifest.build.name,\n            version=manifest.build.version,\n            snapshot=args.snapshot,\n            output_dir=output_dir,\n            platform=args.platform,\n            architecture=args.architecture,\n        )\n\n        os.makedirs(target.output_dir, exist_ok=True)\n\n        build_recorder = BuildRecorder(target)\n\n        logging.info(\n            f\"Building {manifest.build.name} ({target.architecture}) into {target.output_dir}\"\n        )\n\n        for component in manifest.components:\n\n            if args.component and args.component != component.name:\n                logging.info(f\"Skipping {component.name}\")\n                continue\n\n            logging.info(f\"Building {component.name}\")\n\n            with GitRepository(\n                component.repository,\n                component.ref,\n                os.path.join(work_dir.name, component.name),\n                component.working_directory,\n            ) as repo:\n                try:\n                    builder = Builder(component.name, repo, build_recorder)\n                    builder.build(target)\n                    builder.export_artifacts()\n                except:\n                    logging.error(f\"Error building {component.name}, retry with: {args.component_command(component.name)}\")\n                    raise\n\n        build_recorder.write_manifest()\n\n    logging.info(\"Done.\")\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n", "path": "src/run_build.py"}]}