diff --git a/docs/commands.md b/docs/commands.md index 2efbe9751b..4b000a83eb 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -148,6 +148,7 @@ It does not support: It accepts the following parameters: +* `--stage` - [optional] flag to choose staging splunkbase to publish the app. * `--app-id` - [required] Splunkbase numerical app id listed in the URL of the app details page. * `--package-path` - [required] path to the package file (.tar.gz) to be published (must be a valid UCC package). * `--splunk-versions` - [required] comma-separated list of supported Splunk versions (e.g. "9.1,9.2"). diff --git a/splunk_add_on_ucc_framework/commands/publish.py b/splunk_add_on_ucc_framework/commands/publish.py index 26a7233e7e..181bc99301 100644 --- a/splunk_add_on_ucc_framework/commands/publish.py +++ b/splunk_add_on_ucc_framework/commands/publish.py @@ -62,6 +62,7 @@ def encode_multipart_formdata( def upload_package( + base_url: str, app_id: int, package_path: str, splunk_versions: str, @@ -70,7 +71,7 @@ def upload_package( username: str, password: str, ) -> str: - upload_url = f"https://splunkbase.splunk.com/api/v1/app/{app_id}/new_release/" + upload_url = f"{base_url}/app/{app_id}/new_release/" fields = { "filename": os.path.basename(package_path), @@ -109,9 +110,9 @@ def upload_package( def check_package_validation( - package_upload_id: str, username: str, password: str + base_url: str, package_upload_id: str, username: str, password: str ) -> None: - url = f"https://splunkbase.splunk.com/api/v1/package/{package_upload_id}/" + url = f"{base_url}/package/{package_upload_id}/" auth_header = base64.b64encode(f"{username}:{password}".encode()).decode("utf-8") context = ssl.create_default_context(cafile=certifi.where()) @@ -121,7 +122,12 @@ def check_package_validation( try: with urllib.request.urlopen(request, context=context) as response: response_data = json.loads(response.read().decode("utf-8")) - logger.info("Validation status: {}".format(response_data.get("message"))) + if response_data.get("result") == "pass": + logger.info( + "Validation status: {}".format(response_data.get("message")) + ) + else: + raise Exception(response_data.get("message")) except urllib.error.HTTPError as e: error_msg = e.read().decode() logger.error(f"Failed to retrieve package validation status. {error_msg}") @@ -129,6 +135,7 @@ def check_package_validation( def publish_package( + use_stage: bool, app_id: int, package_path: str, splunk_versions: str, @@ -137,7 +144,12 @@ def publish_package( username: str, password: str, ) -> None: + if use_stage: + API_BASEURL = "https://classic.stage.splunkbase.splunk.com/api/v1" + else: + API_BASEURL = "https://splunkbase.splunk.com/api/v1" package_upload_id = upload_package( + API_BASEURL, app_id, package_path, splunk_versions, @@ -147,4 +159,4 @@ def publish_package( password, ) if package_upload_id: - check_package_validation(package_upload_id, username, password) + check_package_validation(API_BASEURL, package_upload_id, username, password) diff --git a/splunk_add_on_ucc_framework/main.py b/splunk_add_on_ucc_framework/main.py index 8d34ef1782..58368276be 100644 --- a/splunk_add_on_ucc_framework/main.py +++ b/splunk_add_on_ucc_framework/main.py @@ -260,6 +260,12 @@ def main(argv: Optional[Sequence[str]] = None) -> int: publish_parser = subparsers.add_parser( "publish", description="Publish package to the Splunkbase" ) + publish_parser.add_argument( + "--stage", + dest="stage", + help="Whether to release the app on staging or production splunkbase.", + action="store_true", + ) publish_parser.add_argument( "--app-id", type=int, @@ -334,6 +340,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int: ) if args.command == "publish": publish.publish_package( + use_stage=args.stage, app_id=args.app_id, package_path=args.package_path, splunk_versions=args.splunk_versions, diff --git a/tests/unit/commands/test_publish.py b/tests/unit/commands/test_publish.py index 76cadbaeb6..34bd825786 100644 --- a/tests/unit/commands/test_publish.py +++ b/tests/unit/commands/test_publish.py @@ -23,6 +23,7 @@ def test_upload_package_success(self, mock_file, mock_urlopen, mock_logger): mock_urlopen.return_value.__exit__ = MagicMock(return_value=None) pkg_id = upload_package( + base_url="https://dummy_url", app_id=1001, package_path="tests/test_package.tgz", splunk_versions="9.5", @@ -48,7 +49,14 @@ def test_upload_package_no_id(self, mock_file, mock_urlopen, mock_logger): mock_urlopen.return_value.__exit__ = MagicMock(return_value=None) pkg_id = upload_package( - 1001, "tests/test_package.tgz", "9.5", "6.x", True, "user", "pass" + "https://dummy_url", + 1001, + "tests/test_package.tgz", + "9.5", + "6.x", + True, + "user", + "pass", ) assert pkg_id == "" mock_file.assert_called_once_with("tests/test_package.tgz", "rb") @@ -75,7 +83,14 @@ def test_upload_package_http_error(self, mock_file, mock_urlopen, mock_logger): with pytest.raises(urllib.error.HTTPError): upload_package( - 1001, "tests/test_package.tgz", "9.5", "6.x", True, "user", "pass" + "https://dummy_url", + 1001, + "tests/test_package.tgz", + "9.5", + "6.x", + True, + "user", + "pass", ) mock_file.assert_called_once_with("tests/test_package.tgz", "rb") mock_logger.error.assert_called_with( @@ -89,13 +104,15 @@ class TestPackageValidation: def test_check_package_validation_success(self, mock_urlopen, mock_logger): mock_response = MagicMock() mock_response.read.return_value = json.dumps( - {"message": "Validation passed"} + {"message": "Validation passed", "result": "pass"} ).encode("utf-8") mock_urlopen.return_value = mock_response mock_urlopen.return_value.__enter__ = MagicMock(return_value=mock_response) mock_urlopen.return_value.__exit__ = MagicMock(return_value=None) - check_package_validation("pkg123", "user", "pass") # should not raise + check_package_validation( + "https://dummy_url", "pkg123", "user", "pass" + ) # should not raise mock_logger.info.assert_called_with("Validation status: Validation passed") @patch("splunk_add_on_ucc_framework.commands.publish.logger") @@ -115,7 +132,7 @@ def test_check_package_validation_http_error(self, mock_urlopen, mock_logger): mock_urlopen.return_value.__exit__ = MagicMock(return_value=None) with pytest.raises(urllib.error.HTTPError): - check_package_validation("pkg123", "user", "pass") + check_package_validation("https://dummy_url", "pkg123", "user", "pass") mock_logger.error.assert_called_with( f"Failed to retrieve package validation status. {mock_error.read().decode()}" ) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 9752e5c019..ba5788eaf7 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -525,6 +525,7 @@ def test_package_command(mock_package, args, expected_parameters): ( [ "publish", + "--stage", "--app-id", "123", "--package-path", @@ -539,6 +540,7 @@ def test_package_command(mock_package, args, expected_parameters): "pass", ], { + "use_stage": True, "app_id": 123, "package_path": "dist/app.tar.gz", "splunk_versions": "9.5", @@ -566,6 +568,7 @@ def test_package_command(mock_package, args, expected_parameters): "--make-visible", ], { + "use_stage": False, "app_id": 456, "package_path": "/tmp/app.tar.gz", "splunk_versions": "9.1,9.2",