Repository: opensearch-project/opensearch-build

Issue:
[META] Automate artifact signing with OpensearchSignerClient for linux
### Is your feature request related to a problem? Please describe

Signing currently is a long painstaking process. It needs to be automated to save time when releasing artifacts. We need a tool to be able to sign all artifacts - 
- opensearch
- opensearch-dashboards

### Describe the solution you'd like

Use the existing `Signer.py` class to sign the artifacts and generate ".sig" file along with ".asc" files.

### Tasks
- [x] #1382
- [x] #1383
- [x] #1385

### Acceptance Criteria
- [ ] User is able to provide the artifact directory and tool is able to sign all the artifacts in the directory
- [ ] User is able to pass the artifact path and the tool is able to sign the artifact

### Next Steps
Next steps would include to extend this process over for mac and windows


Discussion:

Resolving patch:
diff --git a/src/run_sign.py b/src/run_sign.py
--- a/src/run_sign.py
+++ b/src/run_sign.py
@@ -8,19 +8,22 @@
 
 import argparse
 import logging
-import os
 import sys
+from pathlib import Path
 
-from manifests.build_manifest import BuildManifest
+from sign_workflow.sign_artifacts import SignArtifacts
 from sign_workflow.signer import Signer
 from system import console
 
+ACCEPTED_SIGNATURE_FILE_TYPES = [".sig"]
+
 
 def main():
     parser = argparse.ArgumentParser(description="Sign artifacts")
-    parser.add_argument("manifest", type=argparse.FileType("r"), help="Path to local manifest file.")
+    parser.add_argument("target", type=Path, help="Path to local manifest file or artifact directory.")
     parser.add_argument("--component", nargs="?", help="Component name")
     parser.add_argument("--type", nargs="?", help="Artifact type")
+    parser.add_argument("--sigtype", choices=ACCEPTED_SIGNATURE_FILE_TYPES, help="Type of Signature file", default=".asc")
     parser.add_argument(
         "-v",
         "--verbose",
@@ -34,20 +37,13 @@ def main():
 
     console.configure(level=args.logging_level)
 
-    manifest = BuildManifest.from_file(args.manifest)
-    basepath = os.path.dirname(os.path.abspath(args.manifest.name))
-    signer = Signer()
-
-    for component in manifest.components.select(focus=args.component):
-        logging.info(f"Signing {component.name}")
-
-        for artifact_type in component.artifacts:
-            if args.type and args.type != artifact_type:
-                continue
-
-            signer.sign_artifacts(component.artifacts[artifact_type], basepath)
+    sign = SignArtifacts.from_path(path=args.target,
+                                   component=args.component,
+                                   artifact_type=args.type,
+                                   signature_type=args.sigtype,
+                                   signer=Signer())
 
-    logging.info("Done.")
+    sign.sign()
 
 
 if __name__ == "__main__":
diff --git a/src/sign_workflow/sign_artifacts.py b/src/sign_workflow/sign_artifacts.py
new file mode 100644
--- /dev/null
+++ b/src/sign_workflow/sign_artifacts.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+
+# SPDX-License-Identifier: Apache-2.0
+#
+# The OpenSearch Contributors require contributions made to
+# this file be licensed under the Apache-2.0 license or a
+# compatible open source license.
+
+import logging
+import os
+from abc import abstractmethod
+from pathlib import Path
+
+from manifests.build_manifest import BuildManifest
+
+
+class SignArtifacts:
+    def __init__(self, target: Path, component, artifact_type, signature_type, signer):
+        self.target = target
+        self.component = component
+        self.artifact_type = artifact_type
+        self.signature_type = signature_type
+        self.signer = signer
+
+    @abstractmethod
+    def __sign__(self):
+        pass
+
+    def sign(self):
+        self.__sign__()
+        logging.info("Done.")
+
+    def __sign_artifacts__(self, artifacts, basepath):
+        self.signer.sign_artifacts(artifacts, basepath, self.signature_type)
+
+    def __sign_artifact__(self, artifact, basepath):
+        self.signer.sign_artifact(artifact, basepath, self.signature_type)
+
+    @classmethod
+    def __signer_class__(self, path: Path):
+        if path.is_dir():
+            return SignExistingArtifactsDir
+        elif path.suffix == ".yml":
+            return SignWithBuildManifest
+        else:
+            return SignArtifactsExistingArtifactFile
+
+    @classmethod
+    def from_path(self, path: Path, component, artifact_type, signature_type, signer):
+        klass = self.__signer_class__(path)
+        return klass(path, component, artifact_type, signature_type, signer)
+
+
+class SignWithBuildManifest(SignArtifacts):
+
+    def __sign__(self):
+        manifest = BuildManifest.from_file(self.target.open("r"))
+        basepath = self.target.parent
+        for component in manifest.components.select(focus=self.component):
+            logging.info(f"Signing {component.name}")
+
+            for component_artifact_type in component.artifacts:
+                if self.artifact_type and self.artifact_type != component_artifact_type:
+                    continue
+
+                super().__sign_artifacts__(component.artifacts[component_artifact_type], basepath)
+
+
+class SignArtifactsExistingArtifactFile(SignArtifacts):
+
+    def __sign__(self):
+        artifacts = self.target.name
+        basename = self.target.parent
+        super().__sign_artifact__(artifacts, basename)
+
+
+class SignExistingArtifactsDir(SignArtifacts):
+
+    def __sign__(self):
+        for subdir, dirs, files in os.walk(self.target):
+            super().__sign_artifacts__(files, subdir)
diff --git a/src/sign_workflow/signer.py b/src/sign_workflow/signer.py
--- a/src/sign_workflow/signer.py
+++ b/src/sign_workflow/signer.py
@@ -8,7 +8,6 @@
 
 import logging
 import os
-import pathlib
 
 from git.git_repository import GitRepository
 
@@ -19,7 +18,6 @@
 
 
 class Signer:
-
     ACCEPTED_FILE_TYPES = [".zip", ".jar", ".war", ".pom", ".module", ".tar.gz"]
 
     def __init__(self):
@@ -27,23 +25,24 @@ def __init__(self):
         self.git_repo.execute("./bootstrap")
         self.git_repo.execute("rm config.cfg")
 
-    def sign_artifacts(self, artifacts, basepath):
+    def sign_artifact(self, artifact, basepath, signature_type):
+        self.generate_signature_and_verify(artifact, basepath, signature_type)
+
+    def sign_artifacts(self, artifacts, basepath, signature_type):
         for artifact in artifacts:
             if not self.is_valid_file_type(artifact):
                 logging.info(f"Skipping signing of file ${artifact}")
                 continue
-            location = os.path.join(basepath, artifact)
-            self.sign(location)
-            self.verify(location + ".asc")
+            self.generate_signature_and_verify(artifact, basepath, signature_type)
+
+    def generate_signature_and_verify(self, artifact, basepath, signature_type):
+        location = os.path.join(basepath, artifact)
+        self.sign(location, signature_type)
+        self.verify(location + signature_type)
 
     def is_valid_file_type(self, file_name):
         return any(
-            x
-            in [
-                pathlib.Path(file_name).suffix,
-                "".join(pathlib.Path(file_name).suffixes),
-            ]
-            for x in Signer.ACCEPTED_FILE_TYPES
+            file_name.endswith(x) for x in Signer.ACCEPTED_FILE_TYPES
         )
 
     def get_repo_url(self):
@@ -51,8 +50,8 @@ def get_repo_url(self):
             return "https://${GITHUB_TOKEN}@github.com/opensearch-project/opensearch-signer-client.git"
         return "https://github.com/opensearch-project/opensearch-signer-client.git"
 
-    def sign(self, filename):
-        signature_file = filename + ".asc"
+    def sign(self, filename, signature_type):
+        signature_file = filename + signature_type
         signing_cmd = [
             "./opensearch-signer-client",
             "-i",


Test patch:
diff --git a/tests/test_run_sign.py b/tests/test_run_sign.py
--- a/tests/test_run_sign.py
+++ b/tests/test_run_sign.py
@@ -6,7 +6,7 @@
 
 import os
 import unittest
-from unittest.mock import MagicMock, call, patch
+from unittest.mock import patch
 
 import pytest
 
@@ -30,25 +30,11 @@ def test_usage(self, *mocks):
 
     BUILD_MANIFEST = os.path.join(DATA_PATH, "opensearch-build-1.1.0.yml")
 
-    @patch("os.getcwd", return_value="curdir")
     @patch("argparse._sys.argv", ["run_sign.py", BUILD_MANIFEST])
-    @patch("run_sign.Signer", return_value=MagicMock())
-    def test_main(self, mock_signer, *mocks):
+    @patch("run_sign.SignArtifacts")
+    @patch("run_sign.Signer")
+    def test_main(self, mock_signer, mock_sign_artifacts, *mocks):
         main()
 
-        self.assertEqual(mock_signer.return_value.sign_artifacts.call_count, 21)
-        mock_signer.return_value.sign_artifacts.assert_has_calls(
-            [
-                call(
-                    [
-                        "maven/org/opensearch/common-utils/maven-metadata-local.xml",
-                        "maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0-javadoc.jar",
-                        "maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0-sources.jar",
-                        "maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0.pom",
-                        "maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0.jar",
-                    ],
-                    self.DATA_PATH,
-                )
-            ]
-        )
-        mock_signer.return_value.sign_artifacts.assert_has_calls([call(["plugins/opensearch-index-management-1.1.0.0.zip"], self.DATA_PATH)])
+        mock_sign_artifacts.from_path.assert_called_once()
+        mock_sign_artifacts.from_path.return_value.sign.assert_called_once()
diff --git a/tests/tests_sign_workflow/data/artifacts/tar_dummy_artifact_1.0.0.tar.gz b/tests/tests_sign_workflow/data/artifacts/tar_dummy_artifact_1.0.0.tar.gz
new file mode 100644
diff --git a/tests/tests_sign_workflow/data/artifacts/zip_dummy_artifact_1.1.0.zip b/tests/tests_sign_workflow/data/artifacts/zip_dummy_artifact_1.1.0.zip
new file mode 100644
diff --git a/tests/tests_sign_workflow/data/opensearch-build-1.1.0.yml b/tests/tests_sign_workflow/data/opensearch-build-1.1.0.yml
new file mode 100644
--- /dev/null
+++ b/tests/tests_sign_workflow/data/opensearch-build-1.1.0.yml
@@ -0,0 +1,189 @@
+build:
+  platform: linux
+  architecture: x64
+  id: c3ff7a232d25403fa8cc14c97799c323
+  name: OpenSearch
+  version: 1.1.0
+components:
+  - artifacts:
+      dist:
+        - dist/opensearch-min-1.1.0-linux-x64.tar.gz
+      core-plugins:
+        - core-plugins/discovery-ec2-1.1.0.zip
+        - core-plugins/transport-nio-1.1.0.zip
+        - core-plugins/discovery-gce-1.1.0.zip
+        - core-plugins/analysis-ukrainian-1.1.0.zip
+        - core-plugins/discovery-azure-classic-1.1.0.zip
+        - core-plugins/analysis-phonetic-1.1.0.zip
+        - core-plugins/mapper-murmur3-1.1.0.zip
+        - core-plugins/analysis-kuromoji-1.1.0.zip
+        - core-plugins/analysis-stempel-1.1.0.zip
+        - core-plugins/mapper-annotated-text-1.1.0.zip
+        - core-plugins/repository-hdfs-1.1.0.zip
+        - core-plugins/analysis-icu-1.1.0.zip
+        - core-plugins/mapper-size-1.1.0.zip
+        - core-plugins/ingest-attachment-1.1.0.zip
+        - core-plugins/repository-azure-1.1.0.zip
+        - core-plugins/repository-s3-1.1.0.zip
+        - core-plugins/analysis-nori-1.1.0.zip
+        - core-plugins/store-smb-1.1.0.zip
+        - core-plugins/analysis-smartcn-1.1.0.zip
+        - core-plugins/repository-gcs-1.1.0.zip
+      maven:
+        - maven/org/opensearch/common-utils/maven-metadata-local.xml
+        - maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0-javadoc.jar
+        - maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0-sources.jar
+        - maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0.pom
+        - maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0.jar
+    commit_id: b7334f49d530ffd1a3f7bd0e5832b9b2a9caa583
+    name: OpenSearch
+    ref: 1.x
+    repository: https://github.com/opensearch-project/OpenSearch.git
+    version: 1.1.0.0
+  - artifacts:
+      maven:
+        - maven/org/opensearch/common-utils/maven-metadata-local.xml
+        - maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0-javadoc.jar
+        - maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0-sources.jar
+        - maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0.pom
+        - maven/org/opensearch/common-utils/1.1.0.0/common-utils-1.1.0.0.jar
+    commit_id: 3913d7097934cbfe1fdcf919347f22a597d00b76
+    name: common-utils
+    ref: main
+    repository: https://github.com/opensearch-project/common-utils.git
+    version: 1.1.0.0
+  - artifacts:
+      maven:
+        - maven/org/opensearch/opensearch-job-scheduler/maven-metadata-local.xml
+        - maven/org/opensearch/opensearch-job-scheduler/1.1.0.0/opensearch-job-scheduler-1.1.0.0.jar
+        - maven/org/opensearch/opensearch-job-scheduler/1.1.0.0/opensearch-job-scheduler-1.1.0.0.module
+        - maven/org/opensearch/opensearch-job-scheduler/1.1.0.0/opensearch-job-scheduler-1.1.0.0-sources.jar
+        - maven/org/opensearch/opensearch-job-scheduler/1.1.0.0/opensearch-job-scheduler-1.1.0.0-javadoc.jar
+        - maven/org/opensearch/opensearch-job-scheduler/1.1.0.0/opensearch-job-scheduler-1.1.0.0.pom
+        - maven/org/opensearch/opensearch-job-scheduler-spi/maven-metadata-local.xml
+        - maven/org/opensearch/opensearch-job-scheduler-spi/1.1.0.0/opensearch-job-scheduler-spi-1.1.0.0-javadoc.jar
+        - maven/org/opensearch/opensearch-job-scheduler-spi/1.1.0.0/opensearch-job-scheduler-spi-1.1.0.0-sources.jar
+        - maven/org/opensearch/opensearch-job-scheduler-spi/1.1.0.0/opensearch-job-scheduler-spi-1.1.0.0.pom
+        - maven/org/opensearch/opensearch-job-scheduler-spi/1.1.0.0/opensearch-job-scheduler-spi-1.1.0.0.jar
+      plugins:
+        - plugins/opensearch-job-scheduler-1.1.0.0.zip
+    commit_id: 4504dabfc67dd5628c1451e91e9a1c3c4ca71525
+    name: job-scheduler
+    ref: main
+    repository: https://github.com/opensearch-project/job-scheduler.git
+    version: 1.1.0.0
+  - artifacts:
+      plugins:
+        - plugins/opensearch-sql-1.1.0.0.zip
+    commit_id: d68547d585092af1e053d01e1b834259723cd304
+    name: sql
+    ref: main
+    repository: https://github.com/opensearch-project/sql.git
+    version: 1.1.0.0
+  - artifacts:
+      maven:
+        - maven/org/opensearch/notification/maven-metadata-local.xml
+        - maven/org/opensearch/notification/1.1.0.0/notification-1.1.0.0-sources.jar
+        - maven/org/opensearch/notification/1.1.0.0/notification-1.1.0.0.pom
+        - maven/org/opensearch/notification/1.1.0.0/notification-1.1.0.0.jar
+        - maven/org/opensearch/notification/1.1.0.0/notification-1.1.0.0-javadoc.jar
+      plugins:
+        - plugins/opensearch-alerting-1.1.0.0.zip
+    commit_id: 8024b8b9195f837e49e5bebd7f4a31dfc333eb4d
+    name: alerting
+    ref: main
+    repository: https://github.com/opensearch-project/alerting.git
+    version: 1.1.0.0
+  - artifacts:
+      plugins:
+        - plugins/opensearch-notifications-1.1.0.0.zip
+    commit_id: d0d3e485c4a850f73652a989eeec795b7347fbb6
+    name: notifications
+    ref: main
+    repository: https://github.com/opensearch-project/notifications.git
+    version: 1.1.0.0
+  - artifacts:
+      plugins:
+        - plugins/opensearch-security-1.1.0.0.zip
+    commit_id: 534fffe0e6cf2b33b9abcbc6508e98fc2d077a3d
+    name: security
+    ref: main
+    repository: https://github.com/opensearch-project/security.git
+    version: 1.1.0.0
+  - artifacts:
+      maven:
+        - maven/org/opensearch/performanceanalyzer-rca/maven-metadata-local.xml
+        - maven/org/opensearch/performanceanalyzer-rca/1.1.0.0/performanceanalyzer-rca-1.1.0.0.module
+        - maven/org/opensearch/performanceanalyzer-rca/1.1.0.0/performanceanalyzer-rca-1.1.0.0.jar
+        - maven/org/opensearch/performanceanalyzer-rca/1.1.0.0/performanceanalyzer-rca-1.1.0.0.pom
+    commit_id: 345a10fd4f4e94d6392c925ad95503ba8addd152
+    name: performance-analyzer-rca
+    ref: main
+    repository: https://github.com/opensearch-project/performance-analyzer-rca.git
+    version: 1.1.0.0
+  - artifacts:
+      maven:
+        - maven/org/opensearch/opensearch-performance-analyzer/maven-metadata-local.xml
+        - maven/org/opensearch/opensearch-performance-analyzer/1.1.0.0/opensearch-performance-analyzer-1.1.0.0-javadoc.jar
+        - maven/org/opensearch/opensearch-performance-analyzer/1.1.0.0/opensearch-performance-analyzer-1.1.0.0.pom
+        - maven/org/opensearch/opensearch-performance-analyzer/1.1.0.0/opensearch-performance-analyzer-1.1.0.0.module
+        - maven/org/opensearch/opensearch-performance-analyzer/1.1.0.0/opensearch-performance-analyzer-1.1.0.0.jar
+        - maven/org/opensearch/opensearch-performance-analyzer/1.1.0.0/opensearch-performance-analyzer-1.1.0.0-sources.jar
+      plugins:
+        - plugins/opensearch-performance-analyzer-1.1.0.0.zip
+    commit_id: f184f0bc39302ac38af2585c663d619048f6cffe
+    name: performance-analyzer
+    ref: main
+    repository: https://github.com/opensearch-project/performance-analyzer.git
+    version: 1.1.0.0
+  - artifacts:
+      plugins:
+        - plugins/opensearch-index-management-1.1.0.0.zip
+    commit_id: 7897e9ae9cd5b49535e6a8bbf4c2f73cb458af24
+    name: index-management
+    ref: main
+    repository: https://github.com/opensearch-project/index-management.git
+    version: 1.1.0.0
+  - artifacts:
+      libs:
+        - libs/libKNNIndexV2_0_11.so
+      plugins:
+        - plugins/opensearch-knn-1.1.0.0.zip
+    commit_id: 6a3fdcafd75e63521bcf7893ce908642e1d9fcb6
+    name: k-NN
+    ref: main
+    repository: https://github.com/opensearch-project/k-NN.git
+    version: 1.1.0.0
+  - artifacts:
+      plugins:
+        - plugins/opensearch-anomaly-detection-1.1.0.0.zip
+    commit_id: bedc5b620384163abe272e913705fa23cfd3b3a3
+    name: anomaly-detection
+    ref: main
+    repository: https://github.com/opensearch-project/anomaly-detection.git
+    version: 1.1.0.0
+  - artifacts:
+      plugins:
+        - plugins/opensearch-asynchronous-search-1.1.0.0.zip
+    commit_id: aa344cc1ecdf9ad21d6cb8d9e368361e6a0f3132
+    name: asynchronous-search
+    ref: main
+    repository: https://github.com/opensearch-project/asynchronous-search.git
+    version: 1.1.0.0
+  - artifacts:
+      plugins:
+        - plugins/opensearch-reports-scheduler-1.1.0.0.zip
+    commit_id: 622f334b0724e47f7ffd21cf7e7d521a9f6c949e
+    name: dashboards-reports
+    ref: main
+    repository: https://github.com/opensearch-project/dashboards-reports.git
+    version: 1.1.0.0
+  - artifacts:
+      plugins:
+        - plugins/opensearch-notebooks-1.1.0.0.zip
+    commit_id: 5a996eb8ec8c92e7469cd5e5f87b237352f60f61
+    name: dashboards-notebooks
+    ref: main
+    repository: https://github.com/opensearch-project/dashboards-notebooks.git
+    version: 1.1.0.0
+schema-version: '1.2'
diff --git a/tests/tests_sign_workflow/test_sign_artifacts.py b/tests/tests_sign_workflow/test_sign_artifacts.py
new file mode 100644
--- /dev/null
+++ b/tests/tests_sign_workflow/test_sign_artifacts.py
@@ -0,0 +1,83 @@
+import os
+import unittest
+from pathlib import Path
+from unittest.mock import MagicMock, patch
+
+from sign_workflow.sign_artifacts import SignArtifacts, SignArtifactsExistingArtifactFile, SignExistingArtifactsDir, SignWithBuildManifest
+
+
+class TestSignArtifacts(unittest.TestCase):
+
+    @patch("sign_workflow.signer.GitRepository")
+    @patch("sign_workflow.signer.Signer", return_value=MagicMock())
+    def test_from_path_method(self, mock_signer, *mocks):
+        component = 'maven'
+        artifact_type = 'dummy'
+        sigtype = '.asc'
+
+        klass = SignArtifacts.from_path(Path(r"/dummy/path/manifest.yml"), component, artifact_type, sigtype, mock_signer)
+        self.assertEqual(type(SignWithBuildManifest), type(klass.__class__))
+
+        klass = SignArtifacts.from_path(Path(os.path.dirname(__file__)), component, artifact_type, sigtype, mock_signer)
+        self.assertEqual(type(SignExistingArtifactsDir), type(klass.__class__))
+
+        klass = SignArtifacts.from_path(Path(r"/dummy/path/artifact.tar.gz"), component, artifact_type, sigtype, mock_signer)
+        self.assertEqual(type(SignArtifactsExistingArtifactFile), type(klass.__class__))
+
+    def test_signer_class(self):
+        self.assertIs(SignArtifacts.__signer_class__(
+            Path(r"/dummy/path/manifest.yml")),
+            SignWithBuildManifest)
+
+        self.assertIs(SignArtifacts.__signer_class__(
+            Path(os.path.dirname(__file__))),
+            SignExistingArtifactsDir)
+
+        self.assertIs(SignArtifacts.__signer_class__(
+            Path(r"/dummy/path/artifact.tar.gz")),
+            SignArtifactsExistingArtifactFile)
+
+    def test_sign_with_build_manifest(self):
+        BUILD_MANIFEST = Path(os.path.join(os.path.dirname(__file__), "data/opensearch-build-1.1.0.yml"))
+        sigtype = '.asc'
+        signer = MagicMock()
+        signer_with_manifest = SignWithBuildManifest(target=BUILD_MANIFEST,
+                                                     component="",
+                                                     artifact_type="maven",
+                                                     signature_type=sigtype,
+                                                     signer=signer)
+        signer_with_manifest.sign()
+        expected = [
+            'maven/org/opensearch/opensearch-performance-analyzer/maven-metadata-local.xml',
+            'maven/org/opensearch/opensearch-performance-analyzer/1.1.0.0/opensearch-performance-analyzer-1.1.0.0-javadoc.jar',
+            'maven/org/opensearch/opensearch-performance-analyzer/1.1.0.0/opensearch-performance-analyzer-1.1.0.0.pom',
+            'maven/org/opensearch/opensearch-performance-analyzer/1.1.0.0/opensearch-performance-analyzer-1.1.0.0.module',
+            'maven/org/opensearch/opensearch-performance-analyzer/1.1.0.0/opensearch-performance-analyzer-1.1.0.0.jar',
+            'maven/org/opensearch/opensearch-performance-analyzer/1.1.0.0/opensearch-performance-analyzer-1.1.0.0-sources.jar'
+        ]
+        signer.sign_artifacts.assert_called_with(expected, BUILD_MANIFEST.parent, sigtype)
+
+    def test_sign_existing_artifacts_file(self):
+        path = Path(r"/dummy/path/file.tar.gz")
+        sigtype = '.sig'
+        signer = MagicMock()
+        signer_with_manifest = SignArtifactsExistingArtifactFile(target=path,
+                                                                 component='maven',
+                                                                 artifact_type='dummy',
+                                                                 signature_type=sigtype,
+                                                                 signer=signer)
+        signer_with_manifest.sign()
+        signer.sign_artifact.assert_called_with("file.tar.gz", path.parent, sigtype)
+
+    def test_sign_existing_artifacts_folder(self):
+        path = Path(os.path.join(os.path.dirname(__file__), "data/artifacts"))
+        sigtype = '.sig'
+        signer = MagicMock()
+        signer_with_manifest = SignExistingArtifactsDir(target=path,
+                                                        component='maven',
+                                                        artifact_type='dummy',
+                                                        signature_type=sigtype,
+                                                        signer=signer)
+        signer_with_manifest.sign()
+        expected = ["tar_dummy_artifact_1.0.0.tar.gz", "zip_dummy_artifact_1.1.0.zip"]
+        signer.sign_artifacts.assert_called_with(expected, str(path), sigtype)
diff --git a/tests/tests_sign_workflow/test_signer.py b/tests/tests_sign_workflow/test_signer.py
--- a/tests/tests_sign_workflow/test_signer.py
+++ b/tests/tests_sign_workflow/test_signer.py
@@ -7,7 +7,7 @@
 
 class TestSigner(unittest.TestCase):
     @patch("sign_workflow.signer.GitRepository")
-    def test_accepted_file_types(self, git_repo):
+    def test_accepted_file_types_asc(self, git_repo):
         artifacts = [
             "bad-xml.xml",
             "the-jar.jar",
@@ -20,17 +20,44 @@ def test_accepted_file_types(self, git_repo):
             "something-1.0.0.0.jar",
         ]
         expected = [
-            call(os.path.join("path", "the-jar.jar")),
-            call(os.path.join("path", "the-zip.zip")),
-            call(os.path.join("path", "the-war.war")),
-            call(os.path.join("path", "the-pom.pom")),
-            call(os.path.join("path", "the-module.module")),
-            call(os.path.join("path", "the-tar.tar.gz")),
-            call(os.path.join("path", "something-1.0.0.0.jar")),
+            call(os.path.join("path", "the-jar.jar"), ".asc"),
+            call(os.path.join("path", "the-zip.zip"), ".asc"),
+            call(os.path.join("path", "the-war.war"), ".asc"),
+            call(os.path.join("path", "the-pom.pom"), ".asc"),
+            call(os.path.join("path", "the-module.module"), ".asc"),
+            call(os.path.join("path", "the-tar.tar.gz"), ".asc"),
+            call(os.path.join("path", "something-1.0.0.0.jar"), ".asc"),
         ]
         signer = Signer()
         signer.sign = MagicMock()
-        signer.sign_artifacts(artifacts, "path")
+        signer.sign_artifacts(artifacts, "path", ".asc")
+        self.assertEqual(signer.sign.call_args_list, expected)
+
+    @patch("sign_workflow.signer.GitRepository")
+    def test_accepted_file_types_sig(self, git_repo):
+        artifacts = [
+            "bad-xml.xml",
+            "the-jar.jar",
+            "the-zip.zip",
+            "the-war.war",
+            "the-pom.pom",
+            "the-module.module",
+            "the-tar.tar.gz",
+            "random-file.txt",
+            "something-1.0.0.0.jar",
+        ]
+        expected = [
+            call(os.path.join("path", "the-jar.jar"), ".sig"),
+            call(os.path.join("path", "the-zip.zip"), ".sig"),
+            call(os.path.join("path", "the-war.war"), ".sig"),
+            call(os.path.join("path", "the-pom.pom"), ".sig"),
+            call(os.path.join("path", "the-module.module"), ".sig"),
+            call(os.path.join("path", "the-tar.tar.gz"), ".sig"),
+            call(os.path.join("path", "something-1.0.0.0.jar"), ".sig"),
+        ]
+        signer = Signer()
+        signer.sign = MagicMock()
+        signer.sign_artifacts(artifacts, "path", ".sig")
         self.assertEqual(signer.sign.call_args_list, expected)
 
     @patch(
@@ -44,13 +71,25 @@ def test_signer_checks_out_tool(self, mock_execute, mock_signer):
         mock_execute.assert_has_calls([call("./bootstrap"), call("rm config.cfg")])
 
     @patch("sign_workflow.signer.GitRepository")
-    def test_signer_verify(self, mock_repo):
+    def test_signer_verify_asc(self, mock_repo):
         signer = Signer()
         signer.verify("/path/the-jar.jar.asc")
         mock_repo.assert_has_calls([call().execute("gpg --verify-files /path/the-jar.jar.asc")])
 
     @patch("sign_workflow.signer.GitRepository")
-    def test_signer_sign(self, mock_repo):
+    def test_signer_verify_sig(self, mock_repo):
         signer = Signer()
-        signer.sign("/path/the-jar.jar")
+        signer.verify("/path/the-jar.jar.sig")
+        mock_repo.assert_has_calls([call().execute("gpg --verify-files /path/the-jar.jar.sig")])
+
+    @patch("sign_workflow.signer.GitRepository")
+    def test_signer_sign_asc(self, mock_repo):
+        signer = Signer()
+        signer.sign("/path/the-jar.jar", ".asc")
         mock_repo.assert_has_calls([call().execute("./opensearch-signer-client -i /path/the-jar.jar -o /path/the-jar.jar.asc -p pgp")])
+
+    @patch("sign_workflow.signer.GitRepository")
+    def test_signer_sign_sig(self, mock_repo):
+        signer = Signer()
+        signer.sign("/path/the-jar.jar", ".sig")
+        mock_repo.assert_has_calls([call().execute("./opensearch-signer-client -i /path/the-jar.jar -o /path/the-jar.jar.sig -p pgp")])