From 3324e73756fea8cef89dc952e751948b4a385add Mon Sep 17 00:00:00 2001 From: Lucas Gravley <29484535+admiralAwkbar@users.noreply.github.com> Date: Wed, 28 Oct 2020 10:20:48 -0500 Subject: [PATCH 1/7] adding isort --- dependencies/Pipfile | 1 + 1 file changed, 1 insertion(+) diff --git a/dependencies/Pipfile b/dependencies/Pipfile index be003acf..67935d25 100644 --- a/dependencies/Pipfile +++ b/dependencies/Pipfile @@ -10,6 +10,7 @@ black = ">=20" cfn-lint = "*" flake8 = "*" pylint = "*" +isort = "*" snakefmt = "*" snakemake = "*" yamllint = "*" From dbffb46526db6bc2e0efa89dc23e24d1256593e3 Mon Sep 17 00:00:00 2001 From: Lucas Gravley <29484535+admiralAwkbar@users.noreply.github.com> Date: Wed, 28 Oct 2020 10:22:55 -0500 Subject: [PATCH 2/7] adding tests --- .automation/test/python_isort/README.md | 19 ++++++++++++++++++ .automation/test/python_isort/python_bad_1.py | 20 +++++++++++++++++++ .../test/python_isort/python_good_1.py | 13 ++++++++++++ .../python_isort/reports/expected-PYTHON.tap | 7 +++++++ README.md | 14 +++++++------ TEMPLATES/.isort.cfg | 2 ++ lib/buildFileList.sh | 1 + lib/linter.sh | 6 +++++- lib/linterVersions.sh | 2 +- 9 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 .automation/test/python_isort/README.md create mode 100644 .automation/test/python_isort/python_bad_1.py create mode 100644 .automation/test/python_isort/python_good_1.py create mode 100644 .automation/test/python_isort/reports/expected-PYTHON.tap create mode 100644 TEMPLATES/.isort.cfg diff --git a/.automation/test/python_isort/README.md b/.automation/test/python_isort/README.md new file mode 100644 index 00000000..aa9bb375 --- /dev/null +++ b/.automation/test/python_isort/README.md @@ -0,0 +1,19 @@ +# Python Test Cases + +This folder holds the test cases for **Python**. + +## Additional Docs + +No Additional information is needed for this test case. + +## Good Test Cases + +The test cases denoted: `LANGUAGE_good_FILE.EXTENSION` are all valid, and should pass successfully when linted. + +- **Note:** They are linted utilizing the default linter rules. + +## Bad Test Cases + +The test cases denoted: `LANGUAGE_bad_FILE.EXTENSION` are **NOT** valid, and should trigger errors when linted. + +- **Note:** They are linted utilizing the default linter rules. diff --git a/.automation/test/python_isort/python_bad_1.py b/.automation/test/python_isort/python_bad_1.py new file mode 100644 index 00000000..5bd6f7fe --- /dev/null +++ b/.automation/test/python_isort/python_bad_1.py @@ -0,0 +1,20 @@ +from my_lib import Object + +import os + +from my_lib import Object3 + +from my_lib import Object2 + +import sys + +from third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14 + +import sys + +from __future__ import absolute_import + +from third_party import lib3 + +print("Hey") +print("yo") diff --git a/.automation/test/python_isort/python_good_1.py b/.automation/test/python_isort/python_good_1.py new file mode 100644 index 00000000..01203893 --- /dev/null +++ b/.automation/test/python_isort/python_good_1.py @@ -0,0 +1,13 @@ +from __future__ import absolute_import + +import os +import sys + +from my_lib import Object, Object2, Object3 + +from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, + lib9, lib10, lib11, lib12, lib13, lib14, lib15) + + +print("Hey") +print("yo") diff --git a/.automation/test/python_isort/reports/expected-PYTHON.tap b/.automation/test/python_isort/reports/expected-PYTHON.tap new file mode 100644 index 00000000..1751d70f --- /dev/null +++ b/.automation/test/python_isort/reports/expected-PYTHON.tap @@ -0,0 +1,7 @@ +TAP version 13 +1..2 +not ok 1 - python_bad_1.py + --- + message: ************* Module python_bad_1\npython/python_bad_1.py 15 24 E0001 invalid syntax (, line 15) (syntax-error)\n + ... +ok 2 - python_good_1.py diff --git a/README.md b/README.md index 7cab415b..49806ec5 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Developers on **GitHub** can call the **GitHub Action** to lint their code base | **PHP** | [PHP built-in linter](https://www.php.net/) / [PHP CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) / [PHPStan](https://phpstan.org/n) / [Psalm](https://psalm.dev/) | | **PowerShell** | [PSScriptAnalyzer](https://github.com/PowerShell/Psscriptanalyzer) | | **Protocol Buffers** | [protolint](https://github.com/yoheimuta/protolint) | -| **Python3** | [pylint](https://www.pylint.org/) / [flake8](https://flake8.pycqa.org/en/latest/) / [black](https://github.com/psf/black) | +| **Python3** | [pylint](https://www.pylint.org/) / [flake8](https://flake8.pycqa.org/en/latest/) / [black](https://github.com/psf/black) / [isort](https://pypi.org/project/isort/) | | **R** | [lintr](https://github.com/jimhester/lintr) | | **Raku** | [Raku](https://raku.org) | | **Ruby** | [RuboCop](https://github.com/rubocop-hq/rubocop) | @@ -224,9 +224,10 @@ But if you wish to select or exclude specific linters, we give you full control | **OUTPUT_FORMAT** | `none` | The report format to be generated, besides the stdout one. Output format of tap is currently using v13 of the specification. Supported formats: tap | | **OUTPUT_FOLDER** | `super-linter.report` | The location where the output reporting will be generated to. Output folder must not previously exist. | | **OUTPUT_DETAILS** | `simpler` | What level of details to be reported. Supported formats: simpler or detailed. | -| **PYTHON_PYLINT_CONFIG_FILE** | `.python-lint` | Filename for [pylint configuration](https://pylint.pycqa.org/en/latest/user_guide/run.html?highlight=rcfile#command-line-options) (ex: `.python-lint`, `.pylintrc`) | -| **PYTHON_FLAKE8_CONFIG_FILE** | `.flake8` | Filename for [flake8 configuration](https://flake8.pycqa.org/en/latest/user/configuration.html) (ex: `.flake8`, `tox.ini`) | | **PYTHON_BLACK_CONFIG_FILE** | `.python-black` | Filename for [black configuration](https://github.com/psf/black/blob/master/docs/compatible_configs.md) (ex: `.isort.cfg`, `pyproject.toml`) | +| **PYTHON_FLAKE8_CONFIG_FILE** | `.flake8` | Filename for [flake8 configuration](https://flake8.pycqa.org/en/latest/user/configuration.html) (ex: `.flake8`, `tox.ini`) | +| **PYTHON_ISORT_CONFIG_FILE** | `.isort.cfg` | Filename for [isort configuration](https://pycqa.github.io/isort/docs/configuration/config_files/) (ex: `.isort.cfg`, `pyproject.toml`) | +| **PYTHON_PYLINT_CONFIG_FILE** | `.python-lint` | Filename for [pylint configuration](https://pylint.pycqa.org/en/latest/user_guide/run.html?highlight=rcfile#command-line-options) (ex: `.python-lint`, `.pylintrc`) | | **RUBY_CONFIG_FILE** | `.ruby-lint.yml` | Filename for [rubocop configuration](https://docs.rubocop.org/rubocop/configuration.html) (ex: `.ruby-lint.yml`, `.rubocop.yml`) | | **SNAKEMAKE_SNAKEFMT_CONFIG_FILE**| `.snakefmt.toml` | Filename for [Snakemake configuration](https://github.com/snakemake/snakefmt#configuration) (ex: `pyproject.toml`, `.snakefmt.toml`) | | **TYPESCRIPT_ES_CONFIG_FILE** | `.eslintrc.yml` | Filename for [eslint configuration](https://eslint.org/docs/user-guide/configuring#configuration-file-formats) (ex: `.eslintrc.yml`, `.eslintrc.json`) | @@ -237,7 +238,7 @@ But if you wish to select or exclude specific linters, we give you full control | **VALIDATE_BASH_EXEC** | `true` | Flag to enable or disable the linting process of the Bash language to validate if file is stored as executable. | | **VALIDATE_CLOJURE** | `true` | Flag to enable or disable the linting process of the Clojure language. | | **VALIDATE_CLOUDFORMATION** | `true` | Flag to enable or disable the linting process of the AWS Cloud Formation language. | -| **VALIDATE_COFFEE** | `true` | Flag to enable or disable the linting process of the Coffeescript language. | +| **VALIDATE_COFFEE** | `true` | Flag to enable or disable the linting process of the Coffeescript language. | | **VALIDATE_CSHARP** | `true` | Flag to enable or disable the linting process of the C# language. | | **VALIDATE_CSS** | `true` | Flag to enable or disable the linting process of the CSS language. | | **VALIDATE_DART** | `true` | Flag to enable or disable the linting process of the Dart language. | @@ -267,9 +268,10 @@ But if you wish to select or exclude specific linters, we give you full control | **VALIDATE_PHP_PSALM** | `true` | Flag to enable or disable the linting process of the PHP language. (Utilizing: PSalm) | | **VALIDATE_PROTOBUF** | `true` | Flag to enable or disable the linting process of the Protobuf language. | | **VALIDATE_PYTHON** | `true` | Flag to enable or disable the linting process of the Python language. (Utilizing: pylint) (keep for backward compatibility) | -| **VALIDATE_PYTHON_PYLINT** | `true` | Flag to enable or disable the linting process of the Python language. (Utilizing: pylint) | -| **VALIDATE_PYTHON_FLAKE8** | `true` | Flag to enable or disable the linting process of the Python language. (Utilizing: flake8) | | **VALIDATE_PYTHON_BLACK** | `true` | Flag to enable or disable the linting process of the Python language. (Utilizing: black) | +| **VALIDATE_PYTHON_FLAKE8** | `true` | Flag to enable or disable the linting process of the Python language. (Utilizing: flake8) | +| **VALIDATE_PYTHON_ISORT** | `true` | Flag to enable or disable the linting process of the Python language. (Utilizing: isort) | +| **VALIDATE_PYTHON_PYLINT** | `true` | Flag to enable or disable the linting process of the Python language. (Utilizing: pylint) | | **VALIDATE_POWERSHELL** | `true` | Flag to enable or disable the linting process of the Powershell language. | | **VALIDATE_R** | `true` | Flag to enable or disable the linting process of the R language. | | **VALIDATE_RAKU** | `true` | Flag to enable or disable the linting process of the Raku language. | diff --git a/TEMPLATES/.isort.cfg b/TEMPLATES/.isort.cfg new file mode 100644 index 00000000..b3b28cbc --- /dev/null +++ b/TEMPLATES/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +profile=hug diff --git a/lib/buildFileList.sh b/lib/buildFileList.sh index cd3229a4..fb057f16 100755 --- a/lib/buildFileList.sh +++ b/lib/buildFileList.sh @@ -431,6 +431,7 @@ function BuildFileList() { FILE_ARRAY_PYTHON_BLACK+=("${FILE}") FILE_ARRAY_PYTHON_PYLINT+=("${FILE}") FILE_ARRAY_PYTHON_FLAKE8+=("${FILE}") + FILE_ARRAY_PYTHON_ISORT+=("${FILE}") ###################### # Get the RAKU files # diff --git a/lib/linter.sh b/lib/linter.sh index c4d8b941..f63d4756 100755 --- a/lib/linter.sh +++ b/lib/linter.sh @@ -109,6 +109,8 @@ PYTHON_BLACK_FILE_NAME="${PYTHON_BLACK_CONFIG_FILE:-.python-black}" # shellcheck disable=SC2034 # Variable is referenced indirectly PYTHON_FLAKE8_FILE_NAME="${PYTHON_FLAKE8_CONFIG_FILE:-.flake8}" # shellcheck disable=SC2034 # Variable is referenced indirectly +PYTHON_ISORT_FILE_NAME="${PYTHON_ISORT_CONFIG_FILE:-.isort.cfg}" +# shellcheck disable=SC2034 # Variable is referenced indirectly PYTHON_PYLINT_FILE_NAME="${PYTHON_PYLINT_CONFIG_FILE:-.python-lint}" # shellcheck disable=SC2034 # Variable is referenced indirectly R_FILE_NAME=".lintr" @@ -136,7 +138,7 @@ LANGUAGE_ARRAY=('ANSIBLE' 'ARM' 'BASH' 'BASH_EXEC' 'CLOUDFORMATION' 'CLOJURE' 'C 'DART' 'DOCKERFILE' 'DOCKERFILE_HADOLINT' 'EDITORCONFIG' 'ENV' 'GO' 'GROOVY' 'HTML' 'JAVA' 'JAVASCRIPT_ES' 'JAVASCRIPT_STANDARD' 'JSON' 'JSX' 'KUBERNETES_KUBEVAL' 'KOTLIN' 'LATEX' 'LUA' 'MARKDOWN' 'OPENAPI' 'PERL' 'PHP_BUILTIN' 'PHP_PHPCS' 'PHP_PHPSTAN' 'PHP_PSALM' 'POWERSHELL' - 'PROTOBUF' 'PYTHON_BLACK' 'PYTHON_PYLINT' 'PYTHON_FLAKE8' 'R' 'RAKU' 'RUBY' 'SHELL_SHFMT' 'SNAKEMAKE_LINT' 'SNAKEMAKE_SNAKEFMT' 'STATES' 'SQL' + 'PROTOBUF' 'PYTHON_BLACK' 'PYTHON_PYLINT' 'PYTHON_FLAKE8' 'PYTHON_ISORT' 'R' 'RAKU' 'RUBY' 'SHELL_SHFMT' 'SNAKEMAKE_LINT' 'SNAKEMAKE_SNAKEFMT' 'STATES' 'SQL' 'TEKTON' 'TERRAFORM' 'TERRAFORM_TERRASCAN' 'TERRAGRUNT' 'TSX' 'TYPESCRIPT_ES' 'TYPESCRIPT_STANDARD' 'XML' 'YAML') ############################## @@ -181,6 +183,7 @@ LINTER_NAMES_ARRAY['PROTOBUF']="protolint" LINTER_NAMES_ARRAY['PYTHON_BLACK']="black" LINTER_NAMES_ARRAY['PYTHON_PYLINT']="pylint" LINTER_NAMES_ARRAY['PYTHON_FLAKE8']="flake8" +LINTER_NAMES_ARRAY['PYTHON_ISORT']="isort" LINTER_NAMES_ARRAY['R']="R" LINTER_NAMES_ARRAY['RAKU']="raku" LINTER_NAMES_ARRAY['RUBY']="rubocop" @@ -1183,6 +1186,7 @@ LINTER_COMMANDS_ARRAY['PROTOBUF']="protolint lint --config_path ${PROTOBUF_LINTE LINTER_COMMANDS_ARRAY['PYTHON_BLACK']="black --config ${PYTHON_BLACK_LINTER_RULES} --diff --check" LINTER_COMMANDS_ARRAY['PYTHON_PYLINT']="pylint --rcfile ${PYTHON_PYLINT_LINTER_RULES}" LINTER_COMMANDS_ARRAY['PYTHON_FLAKE8']="flake8 --config=${PYTHON_FLAKE8_LINTER_RULES}" +LINTER_COMMANDS_ARRAY['PYTHON_ISORT']="isort --check --diff --sp ${PYTHON_ISORT_LINTER_RULES}" LINTER_COMMANDS_ARRAY['R']="lintr" LINTER_COMMANDS_ARRAY['RAKU']="raku" LINTER_COMMANDS_ARRAY['RUBY']="rubocop -c ${RUBY_LINTER_RULES} --force-exclusion" diff --git a/lib/linterVersions.sh b/lib/linterVersions.sh index d21a2dc3..9046895c 100755 --- a/lib/linterVersions.sh +++ b/lib/linterVersions.sh @@ -23,7 +23,7 @@ ARM_TTK_PSD1='/usr/bin/arm-ttk' # Powershell var ####################################### LINTER_ARRAY=('ansible-lint' 'arm-ttk' 'asl-validator' 'bash-exec' 'black' 'cfn-lint' 'checkstyle' 'chktex' 'clj-kondo' 'coffeelint' 'dotnet-format' 'dart' 'dockerfilelint' 'dotenv-linter' 'editorconfig-checker' 'eslint' 'flake8' 'golangci-lint' - 'hadolint' 'htmlhint' 'jsonlint' 'kubeval' 'ktlint' 'lintr' 'lua' 'markdownlint' 'npm-groovy-lint' 'perl' 'protolint' + 'hadolint' 'htmlhint' 'isort' 'jsonlint' 'kubeval' 'ktlint' 'lintr' 'lua' 'markdownlint' 'npm-groovy-lint' 'perl' 'protolint' 'pwsh' 'pylint' 'raku' 'rubocop' 'shellcheck' 'shfmt' 'spectral' 'standard' 'stylelint' 'sql-lint' 'tekton-lint' 'terrascan' 'tflint' 'xmllint' 'yamllint') From 22dda4056236d718ee48635b0739b29aec179f39 Mon Sep 17 00:00:00 2001 From: Lucas Gravley <29484535+admiralAwkbar@users.noreply.github.com> Date: Wed, 28 Oct 2020 10:50:02 -0500 Subject: [PATCH 3/7] fix tests --- .automation/test/python_isort/python_good_1.py | 6 ++---- TEMPLATES/.isort.cfg | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.automation/test/python_isort/python_good_1.py b/.automation/test/python_isort/python_good_1.py index 01203893..9d6689f4 100644 --- a/.automation/test/python_isort/python_good_1.py +++ b/.automation/test/python_isort/python_good_1.py @@ -4,10 +4,8 @@ import os import sys from my_lib import Object, Object2, Object3 - -from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, - lib9, lib10, lib11, lib12, lib13, lib14, lib15) - +from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, + lib10, lib11, lib12, lib13, lib14, lib15) print("Hey") print("yo") diff --git a/TEMPLATES/.isort.cfg b/TEMPLATES/.isort.cfg index b3b28cbc..dd6b520a 100644 --- a/TEMPLATES/.isort.cfg +++ b/TEMPLATES/.isort.cfg @@ -1,2 +1 @@ [settings] -profile=hug From d78cdc22ac1cbc173afce90863599ac3534c07d1 Mon Sep 17 00:00:00 2001 From: Lucas Gravley <29484535+admiralAwkbar@users.noreply.github.com> Date: Wed, 28 Oct 2020 11:30:03 -0500 Subject: [PATCH 4/7] better tests --- .automation/test/python_isort/python_bad_1.py | 203 ++++++++++++++++-- .../test/python_isort/python_good_1.py | 200 ++++++++++++++++- ...PYTHON.tap => expected-PYTHON.tap.ignored} | 0 3 files changed, 381 insertions(+), 22 deletions(-) rename .automation/test/python_isort/reports/{expected-PYTHON.tap => expected-PYTHON.tap.ignored} (100%) diff --git a/.automation/test/python_isort/python_bad_1.py b/.automation/test/python_isort/python_bad_1.py index 5bd6f7fe..8106d8d8 100644 --- a/.automation/test/python_isort/python_bad_1.py +++ b/.automation/test/python_isort/python_bad_1.py @@ -1,20 +1,195 @@ -from my_lib import Object - -import os - -from my_lib import Object3 - -from my_lib import Object2 - +import json +from os import getenv, path +from pprint import pprint import sys -from third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14 +import click # pylint: disable=import-error +from dotenv import load_dotenv # pylint: disable=import-error +import requests # pylint: disable=import-error -import sys +env = load_dotenv() +api_url = getenv("API_URL", default="https://api.github.com/graphql") +github_token = getenv("GITHUB_TOKEN", default=None) -from __future__ import absolute_import +if github_token is None: + sys.exit( + "GitHub Token is not set." + + "Please set the GITHUB_TOKEN env variable in your system or " + + "the .env file of your project." + ) -from third_party import lib3 +client_id = getenv("CLIENT_ID", default="copy_labels.py") +headers = { + "Authorization": "bearer {github_token}".format(github_token=github_token), + "Accept": "application/vnd.github.bane-preview+json", + "Content-Type": "application/json", +} -print("Hey") -print("yo") + +def create_label(repo_id, label): + """ + Create label in the supplied repo. + + :param repo_id: Unique ID that represents the repo in GitHub + :type repo_id: str + :param label: Object with label information. + :type label: dict + :return: GitHub API request response + """ + + query_variables = { + "createLabelInput": { + "color": label["color"], + "description": label["description"], + "name": label["name"], + "repositoryId": repo_id, + } + } + + with open( + path.join(path.dirname(__file__), "queries/create_label.gql"), "r" + ) as query_file: + query = "".join(query_file.readlines()) + + payload = {"query": query, "variables": query_variables} + response = requests.post(api_url, data=json.dumps(payload), headers=headers).json() + print("Created label {label}".format(label=label["name"])) + + return response + + +def get_labels(owner, repo): + """ + Gets a list of labels from the supplied repo. + :param owner: Repo owner GitHub login. + :type owner: str + :param repo: Repository name. + :type repo: str + :return: A tuple with the GitHub id for the repository and a list of labels defined in the repository + """ + + query_variables = { + "owner": owner, + "name": repo, + } + + with open( + path.join(path.dirname(__file__), "queries/get_repo_data.gql"), "r" + ) as query_file: + query = "".join(query_file.readlines()) + + payload = {"query": query, "variables": query_variables} + response = requests.post(api_url, data=json.dumps(payload), headers=headers) + + status_code = response.status_code + result = response.json() + + if status_code >= 200 and status_code <= 300: + repo_id = result["data"]["repository"]["id"] + labels = result["data"]["repository"]["labels"]["nodes"] + + return repo_id, labels + else: + raise Exception( + "[ERROR] getting issue labels. Status Code: {status_code} - Message: {result}".format( + status_code=status_code, result=result["message"] + ) + ) + + +def delete_label(label_id): + """ + Delete the specified label + :param label_id: Label's node id. + :type label_id: str + :return: GitHub API request response. + """ + + query_variables = { + "deleteLabelInput": {"clientMutationId": client_id, "id": label_id} + } + + with open( + path.join(path.dirname(__file__), "queries/delete_label.gql"), "r" + ) as query_file: + query = "".join(query_file.readlines()) + + payload = {"query": query, "variables": query_variables} + result = requests.post(api_url, data=json.dumps(payload), headers=headers).json() + + return result + + +@click.command() +@click.option("--dry", is_flag=True) +@click.argument("source_repo") +@click.argument("target_repo") +def copy_labels(source_repo, target_repo, dry): + """ + Copy labels from the source repository to the target repository. + \f + :param source: The full name of a GitHub repo from where the labels will be copied from. Eg. github/opensourcefriday + :type source: str + :param target: The full name of a GitHub repo to where the labels will be copied. Eg. github/opensourcefriday + :type target: str + :return: + """ + source_owner, source_repo_name = source_repo.split("/") + target_owner, target_repo_name = target_repo.split("/") + + try: + print( + "Fetching labels for {source_repo_name} repo.".format( + source_repo_name=source_repo_name + ) + ) + _, source_repo_labels = get_labels(source_owner, source_repo_name) + print( + "Fetched labels for {source_repo_name}".format( + source_repo_name=source_repo_name + ) + ) + + print( + "Fetching labels for {target_repo_name} repo.".format( + target_repo_name=target_repo_name + ) + ) + target_repo_id, target_repo_labels = get_labels(target_owner, target_repo_name) + print( + "Fetched labels for {target_repo_name}".format( + target_repo_name=target_repo_name + ) + ) + + filtered_labels = list( + filter(lambda x: x not in target_repo_labels, source_repo_labels) + ) + + if dry: + print("This is just a dry run. No labels will be copied/created.") + print( + "{label_count} labels would have been created.".format( + label_count=len(filtered_labels) + ) + ) + pprint(filtered_labels, indent=4) + else: + print( + "Preparing to created {label_count} labels in {target_repo}".format( + label_count=len(filtered_labels), target_repo=target_repo + ) + ) + + for label in filtered_labels: + create_label(target_repo_id, label) + except Exception as error: + sys.exit(error) + + print("Done") + + +if __name__ == "__main__": + # Pylint doesn't know that @click.command takes care of injecting the + # function parameters. Disabling Pylint error. + copy_labels() # pylint: disable=no-value-for-parameter diff --git a/.automation/test/python_isort/python_good_1.py b/.automation/test/python_isort/python_good_1.py index 9d6689f4..ebf7a62a 100644 --- a/.automation/test/python_isort/python_good_1.py +++ b/.automation/test/python_isort/python_good_1.py @@ -1,11 +1,195 @@ -from __future__ import absolute_import - -import os +mport json import sys +from os import getenv, path +from pprint import pprint -from my_lib import Object, Object2, Object3 -from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, - lib10, lib11, lib12, lib13, lib14, lib15) +import click # pylint: disable=import-error +import requests # pylint: disable=import-error +from dotenv import load_dotenv # pylint: disable=import-error -print("Hey") -print("yo") +env = load_dotenv() +api_url = getenv("API_URL", default="https://api.github.com/graphql") +github_token = getenv("GITHUB_TOKEN", default=None) + +if github_token is None: + sys.exit( + "GitHub Token is not set." + + "Please set the GITHUB_TOKEN env variable in your system or " + + "the .env file of your project." + ) + +client_id = getenv("CLIENT_ID", default="copy_labels.py") +headers = { + "Authorization": "bearer {github_token}".format(github_token=github_token), + "Accept": "application/vnd.github.bane-preview+json", + "Content-Type": "application/json", +} + + +def create_label(repo_id, label): + """ + Create label in the supplied repo. + + :param repo_id: Unique ID that represents the repo in GitHub + :type repo_id: str + :param label: Object with label information. + :type label: dict + :return: GitHub API request response + """ + + query_variables = { + "createLabelInput": { + "color": label["color"], + "description": label["description"], + "name": label["name"], + "repositoryId": repo_id, + } + } + + with open( + path.join(path.dirname(__file__), "queries/create_label.gql"), "r" + ) as query_file: + query = "".join(query_file.readlines()) + + payload = {"query": query, "variables": query_variables} + response = requests.post(api_url, data=json.dumps(payload), headers=headers).json() + print("Created label {label}".format(label=label["name"])) + + return response + + +def get_labels(owner, repo): + """ + Gets a list of labels from the supplied repo. + :param owner: Repo owner GitHub login. + :type owner: str + :param repo: Repository name. + :type repo: str + :return: A tuple with the GitHub id for the repository and a list of labels defined in the repository + """ + + query_variables = { + "owner": owner, + "name": repo, + } + + with open( + path.join(path.dirname(__file__), "queries/get_repo_data.gql"), "r" + ) as query_file: + query = "".join(query_file.readlines()) + + payload = {"query": query, "variables": query_variables} + response = requests.post(api_url, data=json.dumps(payload), headers=headers) + + status_code = response.status_code + result = response.json() + + if status_code >= 200 and status_code <= 300: + repo_id = result["data"]["repository"]["id"] + labels = result["data"]["repository"]["labels"]["nodes"] + + return repo_id, labels + else: + raise Exception( + "[ERROR] getting issue labels. Status Code: {status_code} - Message: {result}".format( + status_code=status_code, result=result["message"] + ) + ) + + +def delete_label(label_id): + """ + Delete the specified label + :param label_id: Label's node id. + :type label_id: str + :return: GitHub API request response. + """ + + query_variables = { + "deleteLabelInput": {"clientMutationId": client_id, "id": label_id} + } + + with open( + path.join(path.dirname(__file__), "queries/delete_label.gql"), "r" + ) as query_file: + query = "".join(query_file.readlines()) + + payload = {"query": query, "variables": query_variables} + result = requests.post(api_url, data=json.dumps(payload), headers=headers).json() + + return result + + +@click.command() +@click.option("--dry", is_flag=True) +@click.argument("source_repo") +@click.argument("target_repo") +def copy_labels(source_repo, target_repo, dry): + """ + Copy labels from the source repository to the target repository. + \f + :param source: The full name of a GitHub repo from where the labels will be copied from. Eg. github/opensourcefriday + :type source: str + :param target: The full name of a GitHub repo to where the labels will be copied. Eg. github/opensourcefriday + :type target: str + :return: + """ + source_owner, source_repo_name = source_repo.split("/") + target_owner, target_repo_name = target_repo.split("/") + + try: + print( + "Fetching labels for {source_repo_name} repo.".format( + source_repo_name=source_repo_name + ) + ) + _, source_repo_labels = get_labels(source_owner, source_repo_name) + print( + "Fetched labels for {source_repo_name}".format( + source_repo_name=source_repo_name + ) + ) + + print( + "Fetching labels for {target_repo_name} repo.".format( + target_repo_name=target_repo_name + ) + ) + target_repo_id, target_repo_labels = get_labels(target_owner, target_repo_name) + print( + "Fetched labels for {target_repo_name}".format( + target_repo_name=target_repo_name + ) + ) + + filtered_labels = list( + filter(lambda x: x not in target_repo_labels, source_repo_labels) + ) + + if dry: + print("This is just a dry run. No labels will be copied/created.") + print( + "{label_count} labels would have been created.".format( + label_count=len(filtered_labels) + ) + ) + pprint(filtered_labels, indent=4) + else: + print( + "Preparing to created {label_count} labels in {target_repo}".format( + label_count=len(filtered_labels), target_repo=target_repo + ) + ) + + for label in filtered_labels: + create_label(target_repo_id, label) + except Exception as error: + sys.exit(error) + + print("Done") + + +if __name__ == "__main__": + # Pylint doesn't know that @click.command takes care of injecting the + # function parameters. Disabling Pylint error. + copy_labels() # pylint: disable=no-value-for-parameter diff --git a/.automation/test/python_isort/reports/expected-PYTHON.tap b/.automation/test/python_isort/reports/expected-PYTHON.tap.ignored similarity index 100% rename from .automation/test/python_isort/reports/expected-PYTHON.tap rename to .automation/test/python_isort/reports/expected-PYTHON.tap.ignored From d906562960a1cf22aa56c30cb08ed3399f17601b Mon Sep 17 00:00:00 2001 From: Lucas Gravley <29484535+admiralAwkbar@users.noreply.github.com> Date: Wed, 28 Oct 2020 11:47:37 -0500 Subject: [PATCH 5/7] better tests --- .automation/test/python_isort/python_good_1.py | 2 +- TEMPLATES/.isort.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.automation/test/python_isort/python_good_1.py b/.automation/test/python_isort/python_good_1.py index ebf7a62a..b3b4e7d1 100644 --- a/.automation/test/python_isort/python_good_1.py +++ b/.automation/test/python_isort/python_good_1.py @@ -1,4 +1,4 @@ -mport json +import json import sys from os import getenv, path from pprint import pprint diff --git a/TEMPLATES/.isort.cfg b/TEMPLATES/.isort.cfg index dd6b520a..8d4634ea 100644 --- a/TEMPLATES/.isort.cfg +++ b/TEMPLATES/.isort.cfg @@ -1 +1 @@ -[settings] +[isort] From 8bbd4d1d078fdcc825a052e57d90cd823030795e Mon Sep 17 00:00:00 2001 From: Lucas Gravley <29484535+admiralAwkbar@users.noreply.github.com> Date: Wed, 28 Oct 2020 12:10:12 -0500 Subject: [PATCH 6/7] fixtemplate --- TEMPLATES/.isort.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/TEMPLATES/.isort.cfg b/TEMPLATES/.isort.cfg index 8d4634ea..a865f55c 100644 --- a/TEMPLATES/.isort.cfg +++ b/TEMPLATES/.isort.cfg @@ -1 +1,2 @@ [isort] +profile= From ccafee781708bfb72c3caf867e89352478fded53 Mon Sep 17 00:00:00 2001 From: Lucas Gravley <29484535+admiralAwkbar@users.noreply.github.com> Date: Wed, 28 Oct 2020 12:50:33 -0500 Subject: [PATCH 7/7] fix tests --- .automation/test/python_black/python_good_1.py | 4 ++-- .automation/test/python_flake8/python_good_1.py | 4 ++-- .automation/test/python_pylint/python_good_1.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.automation/test/python_black/python_good_1.py b/.automation/test/python_black/python_good_1.py index 8106d8d8..b3b4e7d1 100644 --- a/.automation/test/python_black/python_good_1.py +++ b/.automation/test/python_black/python_good_1.py @@ -1,11 +1,11 @@ import json +import sys from os import getenv, path from pprint import pprint -import sys import click # pylint: disable=import-error -from dotenv import load_dotenv # pylint: disable=import-error import requests # pylint: disable=import-error +from dotenv import load_dotenv # pylint: disable=import-error env = load_dotenv() api_url = getenv("API_URL", default="https://api.github.com/graphql") diff --git a/.automation/test/python_flake8/python_good_1.py b/.automation/test/python_flake8/python_good_1.py index 8106d8d8..b3b4e7d1 100644 --- a/.automation/test/python_flake8/python_good_1.py +++ b/.automation/test/python_flake8/python_good_1.py @@ -1,11 +1,11 @@ import json +import sys from os import getenv, path from pprint import pprint -import sys import click # pylint: disable=import-error -from dotenv import load_dotenv # pylint: disable=import-error import requests # pylint: disable=import-error +from dotenv import load_dotenv # pylint: disable=import-error env = load_dotenv() api_url = getenv("API_URL", default="https://api.github.com/graphql") diff --git a/.automation/test/python_pylint/python_good_1.py b/.automation/test/python_pylint/python_good_1.py index 8106d8d8..b3b4e7d1 100644 --- a/.automation/test/python_pylint/python_good_1.py +++ b/.automation/test/python_pylint/python_good_1.py @@ -1,11 +1,11 @@ import json +import sys from os import getenv, path from pprint import pprint -import sys import click # pylint: disable=import-error -from dotenv import load_dotenv # pylint: disable=import-error import requests # pylint: disable=import-error +from dotenv import load_dotenv # pylint: disable=import-error env = load_dotenv() api_url = getenv("API_URL", default="https://api.github.com/graphql")