|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +import argparse |
| 4 | +import os |
| 5 | +import sys |
| 6 | +import re |
| 7 | +import json |
| 8 | +import base64 |
| 9 | +import urllib3 |
| 10 | +from github import Github |
| 11 | +from github.PullRequest import PullRequest |
| 12 | +from github.Repository import Repository |
| 13 | +from github.ContentFile import ContentFile |
| 14 | +from github.Branch import Branch |
| 15 | + |
| 16 | +from utils import load_json, CONTENT_ROOT_PATH, timestamped_print |
| 17 | + |
| 18 | +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) |
| 19 | +print = timestamped_print |
| 20 | + |
| 21 | +METADATA = 'pack_metadata.json' |
| 22 | +PACKS = 'Packs' |
| 23 | +SUPPORT = 'support' |
| 24 | +XSOAR_SUPPORT = 'xsoar' |
| 25 | +PACK_NAME_REGEX = re.compile(r'Packs/([A-Za-z0-9-_.]+)/') |
| 26 | + |
| 27 | + |
| 28 | +def get_metadata_filename_from_pr(pr_files, pack_name) -> str: |
| 29 | + """ Iterates over all pr files and return the pr metadata.json filename if exists, else None |
| 30 | +
|
| 31 | + Args: |
| 32 | + pr_files (PaginatedList[File]): The list of pr files |
| 33 | + pack_name (str): The pack name |
| 34 | +
|
| 35 | + Returns: |
| 36 | + The pr metadata.json filename if exists, else None |
| 37 | +
|
| 38 | + """ |
| 39 | + pack_metadata_path = os.path.join(PACKS, pack_name, METADATA) |
| 40 | + print(f'Searching for a {pack_metadata_path} file in the PR.') |
| 41 | + |
| 42 | + for file in pr_files: |
| 43 | + if pack_metadata_path in file.filename: |
| 44 | + print(f'Found {METADATA} file in PR for pack {pack_name}: {file.filename}.') |
| 45 | + return file.filename |
| 46 | + |
| 47 | + print(f'Did not find a {pack_metadata_path} file in the PR.') |
| 48 | + return '' |
| 49 | + |
| 50 | + |
| 51 | +def get_pack_support_type_from_pr_metadata_file(pr_metadata_filename: str, pr: PullRequest): |
| 52 | + """ Retrieves the support type from the pr metadata.json file |
| 53 | +
|
| 54 | + Args: |
| 55 | + pr_metadata_filename: The pr metadata.json filename |
| 56 | + pr: The pr |
| 57 | +
|
| 58 | + Returns: |
| 59 | + The support type |
| 60 | +
|
| 61 | + """ |
| 62 | + print(f'Getting support type from {pr_metadata_filename}.') |
| 63 | + _, branch_name = pr.head.label.split(':') |
| 64 | + print(f'Branch name is: {branch_name}') |
| 65 | + contributor_repo: Repository = pr.head.repo |
| 66 | + branch: Branch = contributor_repo.get_branch(branch=branch_name) |
| 67 | + metadata_file: ContentFile = contributor_repo.get_contents(path=pr_metadata_filename, ref=branch.commit.sha) |
| 68 | + metadata_file_content: dict = json.loads(base64.b64decode(metadata_file.content)) |
| 69 | + return metadata_file_content.get(SUPPORT) |
| 70 | + |
| 71 | + |
| 72 | +def get_pack_names_from_pr(pr_files) -> set: |
| 73 | + """ Extracts the pack names from the pr files |
| 74 | +
|
| 75 | + Args: |
| 76 | + pr_files (PaginatedList[File]): The list of pr files |
| 77 | +
|
| 78 | + Returns: |
| 79 | + The set of pack names |
| 80 | +
|
| 81 | + """ |
| 82 | + pack_names = set() |
| 83 | + |
| 84 | + for file in pr_files: |
| 85 | + if PACKS in file.filename: |
| 86 | + pack_names.add(re.findall(PACK_NAME_REGEX, file.filename)[0]) |
| 87 | + |
| 88 | + if not pack_names: |
| 89 | + raise Exception('PR does not contains files prefixed with "Packs".') |
| 90 | + |
| 91 | + return pack_names |
| 92 | + |
| 93 | + |
| 94 | +def get_pack_support_type_from_repo_metadata_file(pack_name): |
| 95 | + """ Retrieves the support type from the repo metadata.json file |
| 96 | +
|
| 97 | + Args: |
| 98 | + pack_name (str): The pack name |
| 99 | +
|
| 100 | + Returns: |
| 101 | + The support type |
| 102 | +
|
| 103 | + """ |
| 104 | + print('Getting support type from the repo.') |
| 105 | + repo_pack_metadata_path: str = os.path.join(CONTENT_ROOT_PATH, PACKS, pack_name, METADATA) |
| 106 | + print(f'{pack_name} pack {METADATA} file is at path: {repo_pack_metadata_path}') |
| 107 | + repo_pack_metadata: dict = load_json(repo_pack_metadata_path) |
| 108 | + return repo_pack_metadata.get(SUPPORT) |
| 109 | + |
| 110 | + |
| 111 | +def arguments_handler(): |
| 112 | + """ Validates and parses script arguments. |
| 113 | +
|
| 114 | + Returns: |
| 115 | + Namespace: Parsed arguments object. |
| 116 | +
|
| 117 | + """ |
| 118 | + parser = argparse.ArgumentParser(description='Check if the contribution form needs to be filled.') |
| 119 | + parser.add_argument('-p', '--pr_number', help='The PR number to check if the contribution form needs to be filled.') |
| 120 | + parser.add_argument('-g', '--github_token', help='The GitHub token to authenticate the GitHub client.') |
| 121 | + return parser.parse_args() |
| 122 | + |
| 123 | + |
| 124 | +def main(): |
| 125 | + options = arguments_handler() |
| 126 | + pr_number = options.pr_number |
| 127 | + github_token = options.github_token |
| 128 | + |
| 129 | + org_name: str = 'demisto' |
| 130 | + repo_name: str = 'content' |
| 131 | + exit_status = 0 |
| 132 | + packs_without_metadata_or_support = set() |
| 133 | + not_filled_packs = set() |
| 134 | + |
| 135 | + github_client: Github = Github(github_token, verify=False) |
| 136 | + content_repo: Repository = github_client.get_repo(f'{org_name}/{repo_name}') |
| 137 | + pr: PullRequest = content_repo.get_pull(int(pr_number)) |
| 138 | + pr_files = pr.get_files() |
| 139 | + |
| 140 | + for pack_name in get_pack_names_from_pr(pr_files): |
| 141 | + if pr_metadata_filename := get_metadata_filename_from_pr(pr_files, pack_name): |
| 142 | + support_type = get_pack_support_type_from_pr_metadata_file(pr_metadata_filename, pr) |
| 143 | + else: |
| 144 | + support_type = get_pack_support_type_from_repo_metadata_file(pack_name) |
| 145 | + |
| 146 | + if not support_type: |
| 147 | + packs_without_metadata_or_support.add(pack_name) |
| 148 | + exit_status = 1 |
| 149 | + elif support_type == XSOAR_SUPPORT: |
| 150 | + print(f'\n{pack_name} pack is XSOAR supported. Contribution form should not be filled for XSOAR supported ' |
| 151 | + f'contributions.') |
| 152 | + else: |
| 153 | + not_filled_packs.add(pack_name) |
| 154 | + print(f'{pack_name} pack is {support_type} supported.') |
| 155 | + exit_status = 1 |
| 156 | + |
| 157 | + if packs_without_metadata_or_support: |
| 158 | + print(f'ERROR: {METADATA} file / pack support is missing for the following packs: ' |
| 159 | + f'{packs_without_metadata_or_support}') |
| 160 | + |
| 161 | + if not_filled_packs: |
| 162 | + print(f'\nERROR: Contribution form was not filled for PR: {pr_number}.\nMake sure to register your contribution' |
| 163 | + f' by filling the contribution registration form in - https://forms.gle/XDfxU4E61ZwEESSMA') |
| 164 | + |
| 165 | + sys.exit(exit_status) |
| 166 | + |
| 167 | + |
| 168 | +if __name__ == "__main__": |
| 169 | + main() |
0 commit comments