diff --git a/.changelog/4373.yml b/.changelog/4373.yml new file mode 100644 index 0000000000..132d4a26d1 --- /dev/null +++ b/.changelog/4373.yml @@ -0,0 +1,4 @@ +changes: + - description: Added RM107 to the new validation format. The validation checks if given readme is consist %%FILL HERE%% substring or not. + type: internal +pr_number: 4373 diff --git a/demisto_sdk/commands/validate/sdk_validation_config.toml b/demisto_sdk/commands/validate/sdk_validation_config.toml index b787e20cd3..ce3b9e6067 100644 --- a/demisto_sdk/commands/validate/sdk_validation_config.toml +++ b/demisto_sdk/commands/validate/sdk_validation_config.toml @@ -33,7 +33,7 @@ select = [ "DO100", "DO101", "DO102", "DO103", "DO104", "DO105", "DO106", "DS100", "DS101", "DS105", "DS106", "DS107", "SC100", "SC105", "SC106", "SC109", - "RM101", "RN103", "RM104", "RM105", "RM106", "RM109", "RM113", "RM114", + "RM101", "RN103", "RM104", "RM105", "RM106", "RM107", "RM109", "RM113", "RM114", "CL100", "GF100", "GF101", "GF102", "IF100", "IF101", "IF102", "IF103", "IF104", "IF105", "IF106", "IF116", @@ -59,7 +59,7 @@ select = [ "PA121", "PA123", "PA125", "PA127", "PA130", "PA131", "PA132", "DO100", "DO101", "DO102", "DO103", "DO104", "SC100", "SC105", "SC106", "SC109", - "RM104", "RM105", "RM113", "RM114", + "RM104", "RM105", "RM107", "RM113", "RM114", "CL100", "GF100", "GF101", "IF100", "IF101", "IF102", "IF103", "IF104", "IF105", "IF106", "IF116", diff --git a/demisto_sdk/commands/validate/tests/RM_validators_test.py b/demisto_sdk/commands/validate/tests/RM_validators_test.py index ff7754452a..d4cc2a0fe3 100644 --- a/demisto_sdk/commands/validate/tests/RM_validators_test.py +++ b/demisto_sdk/commands/validate/tests/RM_validators_test.py @@ -1,7 +1,6 @@ from pathlib import Path import pytest - from demisto_sdk.commands.validate.tests.test_tools import ( REPO, create_integration_object, @@ -21,6 +20,9 @@ from demisto_sdk.commands.validate.validators.RM_validators.RM106_is_contain_demisto_word import ( IsContainDemistoWordValidator, ) +from demisto_sdk.commands.validate.validators.RM_validators.RM107_is_template_sentence_in_readme import ( + IsTemplateNotInReadmeValidator, +) from demisto_sdk.commands.validate.validators.RM_validators.RM108_is_integration_image_path_valid import ( IntegrationRelativeImagePathValidator, ) @@ -43,26 +45,26 @@ "content_items, expected_number_of_failures, expected_msgs", [ ( - [ - create_pack_object(readme_text="This is a valid readme."), - create_pack_object(readme_text=""), - ], - 0, - [], + [ + create_pack_object(readme_text="This is a valid readme."), + create_pack_object(readme_text=""), + ], + 0, + [], ), ( - [create_pack_object(readme_text="Invalid readme\nBSD\nCopyright")], - 1, - [ - "Invalid keywords related to Copyrights (BSD, MIT, Copyright, proprietary) were found in lines: 2, 3. Copyright section cannot be part of pack readme." - ], + [create_pack_object(readme_text="Invalid readme\nBSD\nCopyright")], + 1, + [ + "Invalid keywords related to Copyrights (BSD, MIT, Copyright, proprietary) were found in lines: 2, 3. Copyright section cannot be part of pack readme." + ], ), ], ) def test_IsContainCopyRightSectionValidator_is_valid( - content_items, - expected_number_of_failures, - expected_msgs, + content_items, + expected_number_of_failures, + expected_msgs, ): """ Given @@ -92,48 +94,48 @@ def test_IsContainCopyRightSectionValidator_is_valid( "content_items, expected_number_of_failures, expected_msgs", [ ( - [ - create_pack_object( - paths=["support"], - values=["partner"], - readme_text="This is a valid readme.", - ), # valid readme with partner support - create_pack_object( - readme_text="" - ), # empty readme but with no partner support and no playbooks - create_pack_object( - readme_text="This is valid readme", playbooks=1 - ), # should pass as it has a valid readme and playbooks - ], - 0, - [], + [ + create_pack_object( + paths=["support"], + values=["partner"], + readme_text="This is a valid readme.", + ), # valid readme with partner support + create_pack_object( + readme_text="" + ), # empty readme but with no partner support and no playbooks + create_pack_object( + readme_text="This is valid readme", playbooks=1 + ), # should pass as it has a valid readme and playbooks + ], + 0, + [], ), ( - [ - create_pack_object( - paths=["support"], values=["partner"], readme_text="" - ), - ], # empty readme with partner support, not valid - 1, - [ - "Pack HelloWorld written by a partner or pack containing playbooks must have a full README.md file with pack information. Please refer to https://xsoar.pan.dev/docs/documentation/pack-docs#pack-readme for more information", - ], + [ + create_pack_object( + paths=["support"], values=["partner"], readme_text="" + ), + ], # empty readme with partner support, not valid + 1, + [ + "Pack HelloWorld written by a partner or pack containing playbooks must have a full README.md file with pack information. Please refer to https://xsoar.pan.dev/docs/documentation/pack-docs#pack-readme for more information", + ], ), ( - [ - create_pack_object(readme_text="", playbooks=1) - ], # empty readme with playbooks, not valid - 1, - [ - "Pack HelloWorld written by a partner or pack containing playbooks must have a full README.md file with pack information. Please refer to https://xsoar.pan.dev/docs/documentation/pack-docs#pack-readme for more information" - ], + [ + create_pack_object(readme_text="", playbooks=1) + ], # empty readme with playbooks, not valid + 1, + [ + "Pack HelloWorld written by a partner or pack containing playbooks must have a full README.md file with pack information. Please refer to https://xsoar.pan.dev/docs/documentation/pack-docs#pack-readme for more information" + ], ), ], ) def test_empty_readme_validator( - content_items, - expected_number_of_failures, - expected_msgs, + content_items, + expected_number_of_failures, + expected_msgs, ): """ Given: @@ -167,36 +169,36 @@ def test_empty_readme_validator( [ ([create_integration_object()], 0), ( - [ - create_integration_object( - readme_content='Alt text' - ) - ], - 1, + [ + create_integration_object( + readme_content='Alt text' + ) + ], + 1, ), ( - [ - create_script_object( - readme_content='Alt text' - ) - ], - 1, + [ + create_script_object( + readme_content='Alt text' + ) + ], + 1, ), ( - [ - create_pack_object( - readme_text='Alt text' - ) - ], - 1, + [ + create_pack_object( + readme_text='Alt text' + ) + ], + 1, ), ( - [ - create_playbook_object( - readme_content='Alt text' - ) - ], - 1, + [ + create_playbook_object( + readme_content='Alt text' + ) + ], + 1, ), ], ) @@ -232,58 +234,58 @@ def test_is_image_path_validator(content_items, expected_number_of_failures): "content_items, is_file_exist, expected_number_of_failures, expected_msgs", [ ( - [ - create_playbook_object( - readme_content="This is a valid readme without any images.", - pack_info={"name": "test1"}, - ), - create_playbook_object( - readme_content="This is a valid readme if this file exists ![example image](../doc_files/example.png)", - pack_info={"name": "test1"}, - ), - create_playbook_object(readme_content="", pack_info={"name": "test1"}), - create_integration_object( - readme_content="This is a valid readme without any images.", - pack_info={"name": "test2"}, - ), - create_integration_object( - readme_content="This is a valid readme if this file exists ![example image](../doc_files/example.png)", - pack_info={"name": "test2"}, - ), - create_integration_object( - readme_content="", pack_info={"name": "test2"} - ), - ], - True, - 0, - [], + [ + create_playbook_object( + readme_content="This is a valid readme without any images.", + pack_info={"name": "test1"}, + ), + create_playbook_object( + readme_content="This is a valid readme if this file exists ![example image](../doc_files/example.png)", + pack_info={"name": "test1"}, + ), + create_playbook_object(readme_content="", pack_info={"name": "test1"}), + create_integration_object( + readme_content="This is a valid readme without any images.", + pack_info={"name": "test2"}, + ), + create_integration_object( + readme_content="This is a valid readme if this file exists ![example image](../doc_files/example.png)", + pack_info={"name": "test2"}, + ), + create_integration_object( + readme_content="", pack_info={"name": "test2"} + ), + ], + True, + 0, + [], ), ( - [ - create_playbook_object( - readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.png), ", - pack_info={"name": "test1"}, - ), - create_integration_object( - readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.png)", - pack_info={"name": "test2"}, - ), - ], - False, - 2, - [ - "The following images do not exist: Packs/test1/doc_files/example.png", - "The following images do not exist: Packs/test2/doc_files/example.png", - ], + [ + create_playbook_object( + readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.png), ", + pack_info={"name": "test1"}, + ), + create_integration_object( + readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.png)", + pack_info={"name": "test2"}, + ), + ], + False, + 2, + [ + "The following images do not exist: Packs/test1/doc_files/example.png", + "The following images do not exist: Packs/test2/doc_files/example.png", + ], ), ], ) def test_IsImageExistsInReadmeValidator_is_valid( - mocker, - content_items, - is_file_exist, - expected_number_of_failures, - expected_msgs, + mocker, + content_items, + is_file_exist, + expected_number_of_failures, + expected_msgs, ): mocker.patch.object(Path, "is_file", return_value=is_file_exist) @@ -341,39 +343,39 @@ def test_IsPackReadmeNotEqualPackDescriptionValidator_valid(): "content_items, expected_number_of_failures, expected_msgs", [ ( - [ - create_playbook_object(), - create_playbook_object(), - ], - 1, - [ - "The Playbook 'Detonate File - JoeSecurity V2' doesn't have a README file. Please add a README.md file in the content item's directory." - ], + [ + create_playbook_object(), + create_playbook_object(), + ], + 1, + [ + "The Playbook 'Detonate File - JoeSecurity V2' doesn't have a README file. Please add a README.md file in the content item's directory." + ], ), ( - [ - create_script_object(), - create_script_object(), - ], - 1, - [ - "The Script 'myScript' doesn't have a README file. Please add a README.md file in the content item's directory." - ], + [ + create_script_object(), + create_script_object(), + ], + 1, + [ + "The Script 'myScript' doesn't have a README file. Please add a README.md file in the content item's directory." + ], ), ( - [ - create_integration_object(), - create_integration_object(), - ], - 1, - [ - "The Integration 'TestIntegration' doesn't have a README file. Please add a README.md file in the content item's directory." - ], + [ + create_integration_object(), + create_integration_object(), + ], + 1, + [ + "The Integration 'TestIntegration' doesn't have a README file. Please add a README.md file in the content item's directory." + ], ), ], ) def test_IsReadmeExistsValidator_is_valid( - content_items, expected_number_of_failures, expected_msgs + content_items, expected_number_of_failures, expected_msgs ): """ Given: @@ -478,7 +480,7 @@ def test_ImagePathIntegrationValidator_is_valid_invalid_case(): content_items = [ create_integration_object( readme_content=" Readme contains absolute path:\n 'Here is an image:\n" - " ![Example Image](https://www.example.com/images/example_image.jpg)", + " ![Example Image](https://www.example.com/images/example_image.jpg)", description_content="valid description ![Example Image](../../content/image.jpg)", ), ] @@ -528,8 +530,8 @@ def test_ImagePathOnlyReadMeValidator_is_valid_invalid_case(): content_items = [ create_integration_object( readme_content=" Readme contains absolute path:\n 'Here is an image:\n" - " ![Example Image](https://www.example.com/images/example_image.jpg)" - " ![Example Image](../../content/image.jpg)", + " ![Example Image](https://www.example.com/images/example_image.jpg)" + " ![Example Image](../../content/image.jpg)", ), ] expected = ( @@ -542,3 +544,69 @@ def test_ImagePathOnlyReadMeValidator_is_valid_invalid_case(): result = ReadmeRelativeImagePathValidator().is_valid(content_items) assert result[0].message == expected + + +def test_VerifyTemplateNotInReadmeValidator_valid_case(repo): + """ + Given + content_items. + - Integration with readme that contains %%FILL HERE%% template substring. + - Script with readme that contains %%FILL HERE%% template substring. + - Playbook with readme that contains %%FILL HERE%% template substring. + - Pack with readme that contains %%FILL HERE%% template substring. + When + - Calling the IsTemplateNotInReadmeValidator is_valid function. + + Then + - Make sure that the pack content_items return False, there is %%FILL HERE%% in the readme file. + """ + content_items = [ + create_integration_object( + readme_content="This checks if we have the sentence %%FILL HERE%% in the README.", + ), + create_script_object( + readme_content="This checks if we have the sentence %%FILL HERE%% in the README.", + ), + create_playbook_object( + readme_content="This checks if we have the sentence %%FILL HERE%% in the README.", + ), + create_pack_object( + readme_text="This checks if we have the sentence %%FILL HERE%% in the README.", + ) + ] + assert not IsTemplateNotInReadmeValidator().is_valid(content_items) + + +def test_VerifyTemplateNotInReadmeValidator_invalid_case(repo): + """ + Given + content_items. + - Integration with readme without %%FILL HERE%% template substring. + - Script with readme without %%FILL HERE%% template substring. + - Playbook with readme without %%FILL HERE%% template substring. + - Pack with readme without %%FILL HERE%% template substring. + When + - Calling the IsTemplateNotInReadmeValidator is_valid function. + + Then + - Make sure that the pack content_items return True, there is not %%FILL HERE%% in the readme file. + """ + content_items = [ + create_integration_object( + readme_content="The specific template substring is not in the README.", + ), + create_script_object( + readme_content="The specific template substring is not in the README.", + ), + create_playbook_object( + readme_content="The specific template substring is not in the README.", + ), + create_pack_object( + readme_text="The specific template substring is not in the README.", + ) + ] + expected_error_message = "The template '%%FILL HERE%%' does not exist in the README content." + validator_results = IsTemplateNotInReadmeValidator().is_valid(content_items) + assert validator_results + for validator_result in validator_results: + assert validator_result.message == expected_error_message diff --git a/demisto_sdk/commands/validate/validators/RM_validators/RM107_is_template_sentence_in_readme.py b/demisto_sdk/commands/validate/validators/RM_validators/RM107_is_template_sentence_in_readme.py new file mode 100644 index 0000000000..1ea63d7a30 --- /dev/null +++ b/demisto_sdk/commands/validate/validators/RM_validators/RM107_is_template_sentence_in_readme.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from typing import Iterable, List, Union +from demisto_sdk.commands.content_graph.objects.integration import Integration +from demisto_sdk.commands.content_graph.objects.script import Script +from demisto_sdk.commands.content_graph.objects.playbook import Playbook +from demisto_sdk.commands.content_graph.objects.pack import Pack +from demisto_sdk.commands.validate.validators.base_validator import ( + BaseValidator, + ValidationResult, +) + +from demisto_sdk.commands.common.tools import search_substrings_by_line + +ContentTypes = Union[Integration, Script, Playbook, Pack] + + +class IsTemplateNotInReadmeValidator(BaseValidator[ContentTypes]): + error_code = "RM107" + description = "Checks if there are the generic sentence '%%FILL HERE%%' in the README content." + rationale = "Checks if there are the generic sentence '%%FILL HERE%%' in the README content." + error_message = "The template '%%FILL HERE%%' does not exist in the README content." + related_field = "readme.file_content" + is_auto_fixable = False + + def is_valid(self, content_items: Iterable[ContentTypes]) -> List[ValidationResult]: + """ + Checks if there are the generic sentence '%%FILL HERE%%' in the README content. + + Return: + True if '%%FILL HERE%%' does not exist in the README content, and False if it does. + """ + + return [ + ValidationResult( + validator=self, + message=self.error_message, + content_object=content_item, + ) + for content_item in content_items + if ( + invalid_lines := not search_substrings_by_line( + phrases_to_search=["%%FILL HERE%%"], + text=content_item.readme.file_content + ) + ) + ]