Repository: opensearch-project/opensearch-build

Issue:
[Bug]: The location specified in bundle-manifest (manifest.yml) are invalid
### Describe the bug

A build manifest is bundled inside the OpenSearch bundle tarball (manifest.yml) which contains all the information about the components used to build the bundle. The bundle manifest contains a key `location` whose value is invalid for all components.

```
build:
  architecture: x64
  id: '317'
  location: https://ci.opensearch.org/ci/bundles/1.1.0/317/opensearch-1.1.0-linux-x64.tar.gz
  name: OpenSearch
  version: 1.1.0
components:
- commit_id: 15e9f137622d878b79103df8f82d78d782b686a1
  location: https://ci.opensearch.org/ci/builds/1.1.0/317/bundle/opensearch-min-1.1.0-linux-x64.tar.gz
  name: OpenSearch
  ref: '1.1'
  repository: https://github.com/opensearch-project/OpenSearch.git
```

### To reproduce

Download the bundle. Untar it and see the manifest.yml

Try accessing the location url to download individual component. It will give `Access Denied` error

### Expected behavior

The URLs should be valid. Each component should be downloadable from the given location url

### Screenshots

_No response_

### Host / Environment

_No response_

### Additional context

_No response_

### Relevant log output

_No response_


Discussion:
Fixing... pull request incoming

Resolving patch:
diff --git a/bundle-workflow/src/assemble_workflow/bundle_recorder.py b/bundle-workflow/src/assemble_workflow/bundle_recorder.py
--- a/bundle-workflow/src/assemble_workflow/bundle_recorder.py
+++ b/bundle-workflow/src/assemble_workflow/bundle_recorder.py
@@ -31,8 +31,8 @@ def __get_tar_name(self, build):
         return "-".join(parts) + ".tar.gz"
 
     def __get_public_url_path(self, folder, rel_path):
-        path = "{}/{}/{}/{}".format(folder, self.version, self.build_id, rel_path)
-        return urljoin(self.public_url, path)
+        path = "/".join((folder, self.version, self.build_id, rel_path))
+        return urljoin(self.public_url + "/", path)
 
     def __get_location(self, folder_name, rel_path, abs_path):
         if self.public_url:


Test patch:
diff --git a/bundle-workflow/tests/tests_assemble_workflow/test_bundle_recorder.py b/bundle-workflow/tests/tests_assemble_workflow/test_bundle_recorder.py
--- a/bundle-workflow/tests/tests_assemble_workflow/test_bundle_recorder.py
+++ b/bundle-workflow/tests/tests_assemble_workflow/test_bundle_recorder.py
@@ -87,3 +87,60 @@ def test_write_manifest(self):
             data = self.bundle_recorder.get_manifest().to_dict()
             with open(manifest_path) as f:
                 self.assertEqual(yaml.safe_load(f), data)
+
+    def test_record_component_public(self):
+        self.bundle_recorder.public_url = 'https://ci.opensearch.org/ci/os-distro-prod'
+        component = BuildManifest.Component(
+            {
+                "name": "job_scheduler",
+                "repository": "https://github.com/opensearch-project/job_scheduler",
+                "ref": "main",
+                "commit_id": "3913d7097934cbfe1fdcf919347f22a597d00b76",
+                "artifacts": [],
+                "version": "1.0",
+            }
+        )
+        self.bundle_recorder.record_component(component, "plugins")
+
+        self.assertEqual(
+            self.bundle_recorder.get_manifest().to_dict(),
+            {
+                "build": {
+                    "architecture": "x64",
+                    "id": "c3ff7a232d25403fa8cc14c97799c323",
+                    "location": "output_dir/opensearch-1.1.0-linux-x64.tar.gz",
+                    "name": "OpenSearch",
+                    "version": "1.1.0",
+                },
+                "components": [
+                    {
+                        "commit_id": "3913d7097934cbfe1fdcf919347f22a597d00b76",
+                        "location": "https://ci.opensearch.org/ci/os-distro-prod/builds/1.1.0/c3ff7a232d25403fa8cc14c97799c323/plugins",
+                        "name": component.name,
+                        "ref": "main",
+                        "repository": "https://github.com/opensearch-project/job_scheduler",
+                    }
+                ],
+                "schema-version": "1.0",
+            },
+        )
+
+    def test_get_location_scenarios(self):
+        def get_location(public_url):
+            self.bundle_recorder.public_url = public_url
+            return self.bundle_recorder._BundleRecorder__get_location("builds", "dir1/dir2/file", "/tmp/builds/foo/dir1/dir2/file")
+
+        # No public URL - Fallback to ABS Path
+        self.assertEqual(get_location(None), "/tmp/builds/foo/dir1/dir2/file")
+
+        # Public URL - No trailing slash
+        self.assertEqual(
+            get_location('https://ci.opensearch.org/ci/os-distro-prod'),
+            "https://ci.opensearch.org/ci/os-distro-prod/builds/1.1.0/c3ff7a232d25403fa8cc14c97799c323/dir1/dir2/file"
+        )
+
+        # Public URL - Trailing slash
+        self.assertEqual(
+            get_location('https://ci.opensearch.org/ci/os-distro-prod/'),
+            "https://ci.opensearch.org/ci/os-distro-prod/builds/1.1.0/c3ff7a232d25403fa8cc14c97799c323/dir1/dir2/file"
+        )