From 77891abf2160b7eaf28d124874ca38508cade974 Mon Sep 17 00:00:00 2001 From: beckermr Date: Sat, 21 Sep 2024 08:15:15 -0400 Subject: [PATCH 1/7] feat: move linting to GHA --- conda_forge_webservices/commands.py | 31 ++++++------ conda_forge_webservices/feedstock_outputs.py | 10 ++-- conda_forge_webservices/feedstocks_service.py | 8 +-- conda_forge_webservices/linting.py | 49 ++++++++++++++----- conda_forge_webservices/tokens.py | 12 ++++- conda_forge_webservices/update_teams.py | 4 +- conda_forge_webservices/webapp.py | 40 ++++++++------- 7 files changed, 98 insertions(+), 56 deletions(-) diff --git a/conda_forge_webservices/commands.py b/conda_forge_webservices/commands.py index abab683ea..cc3e7fe49 100644 --- a/conda_forge_webservices/commands.py +++ b/conda_forge_webservices/commands.py @@ -17,6 +17,7 @@ from .utils import ALLOWED_CMD_NON_FEEDSTOCKS, with_action_url from conda_forge_webservices.tokens import ( get_app_token_for_webservices_only, + get_gh_client, inject_app_token_into_feedstock, inject_app_token_into_feedstock_readonly, ) @@ -148,7 +149,7 @@ def add_reaction( def pr_comment(org_name, repo_name, issue_num, comment, comment_id=None): if not COMMAND_PREFIX.search(comment): return - gh = github.Github(get_app_token_for_webservices_only()) + gh = get_gh_client() repo = gh.get_repo(f"{org_name}/{repo_name}") pr = repo.get_pull(int(issue_num)) pr_detailed_comment( @@ -178,10 +179,8 @@ def pr_detailed_comment( if not (repo_name.endswith("-feedstock") or is_allowed_cmd): return - GH_TOKEN = get_app_token_for_webservices_only() - if not is_allowed_cmd: - gh = github.Github(GH_TOKEN) + gh = get_gh_client() repo = gh.get_repo(f"{org_name}/{repo_name}") pull = repo.get_pull(int(pr_num)) if pull.head.repo.full_name.split("/")[0] == "conda-forge": @@ -198,7 +197,7 @@ def pr_detailed_comment( pull.create_issue_comment(message) if RESTART_CI.search(comment): - gh = github.Github(GH_TOKEN) + gh = get_gh_client() repo = gh.get_repo(f"{org_name}/{repo_name}") if comment_id is not None or review_id is not None: add_reaction("rocket", repo, pr_num, comment_id, review_id) @@ -219,7 +218,7 @@ def pr_detailed_comment( else: team = repo_name.replace("-feedstock", "") - gh = github.Github(GH_TOKEN) + gh = get_gh_client() repo = gh.get_repo(f"{org_name}/{repo_name}") if comment_id is not None or review_id is not None: add_reaction("rocket", repo, pr_num, comment_id, review_id) @@ -232,7 +231,7 @@ def pr_detailed_comment( pull.create_issue_comment(message) if not is_allowed_cmd and RERUN_BOT.search(comment): - gh = github.Github(GH_TOKEN) + gh = get_gh_client() repo = gh.get_repo(f"{org_name}/{repo_name}") if comment_id is not None or review_id is not None: add_reaction("rocket", repo, pr_num, comment_id, review_id) @@ -252,16 +251,17 @@ def pr_detailed_comment( return if comment_id is not None or review_id is not None: - repo = github.Github(GH_TOKEN).get_repo(f"{org_name}/{repo_name}") + repo = get_gh_client().get_repo(f"{org_name}/{repo_name}") add_reaction("rocket", repo, pr_num, comment_id, review_id) tmp_dir = None try: tmp_dir = tempfile.mkdtemp("_recipe") + gh_token = get_app_token_for_webservices_only() feedstock_dir = os.path.join(tmp_dir, repo_name) repo_url = ( - f"https://x-access-token:{GH_TOKEN}@github.com/{pr_owner}/{pr_repo}.git" + f"https://x-access-token:{gh_token}@github.com/{pr_owner}/{pr_repo}.git" ) for _git_try_num in range(NUM_GIT_CLONE_TRIES): @@ -342,7 +342,7 @@ def pr_detailed_comment( """).format(doc_url) # noqa if message is not None: - gh = github.Github(GH_TOKEN) + gh = get_gh_client() gh_repo = gh.get_repo(f"{org_name}/{repo_name}") pull = gh_repo.get_pull(int(pr_num)) pull.create_issue_comment(message) @@ -385,8 +385,6 @@ def issue_comment(org_name, repo_name, issue_num, title, comment, comment_id=Non if not any(command.search(text) for command in issue_commands): return - APP_GH_TOKEN = get_app_token_for_webservices_only() - # sometimes the webhook outpaces other bits of the API so we try a bit for i in range(NUM_GH_API_TRIES): try: @@ -405,7 +403,7 @@ def issue_comment(org_name, repo_name, issue_num, title, comment, comment_id=Non raise e # these are used when the app takes actions - app_repo = github.Github(APP_GH_TOKEN).get_repo(f"{org_name}/{repo_name}") + app_repo = get_gh_client().get_repo(f"{org_name}/{repo_name}") app_issue = app_repo.get_issue(int(issue_num)) if comment_id is not None: @@ -458,11 +456,12 @@ def issue_comment(org_name, repo_name, issue_num, title, comment, comment_id=Non gh, ) + gh_token = get_app_token_for_webservices_only() feedstock_dir = os.path.join(tmp_dir, repo_name) repo_url = "https://x-access-token:{}@github.com/{}/{}.git".format( os.environ["GH_TOKEN"], forked_user, repo_name ) - upstream_repo_url = f"https://x-access-token:{APP_GH_TOKEN}@github.com/{org_name}/{repo_name}.git" + upstream_repo_url = f"https://x-access-token:{gh_token}@github.com/{org_name}/{repo_name}.git" for _git_try_num in range(NUM_GIT_CLONE_TRIES): try: @@ -971,7 +970,7 @@ def make_rerender_dummy_commit(repo): def rerender(full_name, pr_num): - gh = github.Github(get_app_token_for_webservices_only()) + gh = get_gh_client() repo = gh.get_repo(full_name) inject_app_token_into_feedstock(full_name, repo=repo) @@ -984,7 +983,7 @@ def rerender(full_name, pr_num): def update_version(full_name, pr_num, input_ver): - gh = github.Github(get_app_token_for_webservices_only()) + gh = get_gh_client() repo = gh.get_repo(full_name) inject_app_token_into_feedstock(full_name, repo=repo) diff --git a/conda_forge_webservices/feedstock_outputs.py b/conda_forge_webservices/feedstock_outputs.py index cd5a9c0b4..dbf926133 100644 --- a/conda_forge_webservices/feedstock_outputs.py +++ b/conda_forge_webservices/feedstock_outputs.py @@ -24,7 +24,10 @@ import binstar_client.errors from .utils import parse_conda_pkg -from conda_forge_webservices.tokens import get_app_token_for_webservices_only +from conda_forge_webservices.tokens import ( + get_app_token_for_webservices_only, + get_gh_client, +) LOGGER = logging.getLogger("conda_forge_webservices.feedstock_outputs") @@ -217,8 +220,7 @@ def _add_feedstock_output( feedstock: str, pkg_name: str, ): - gh_token = get_app_token_for_webservices_only() - gh = github.Github(auth=github.Auth.Token(gh_token)) + gh = get_gh_client() repo = gh.get_repo("conda-forge/feedstock-outputs") try: contents = repo.get_contents(_get_sharded_path(pkg_name)) @@ -431,7 +433,7 @@ def comment_on_outputs_copy(feedstock, git_sha, errors, valid, copied): if not feedstock.endswith("-feedstock"): return None - gh = github.Github(get_app_token_for_webservices_only()) + gh = get_gh_client() team_name = feedstock[: -len("-feedstock")] diff --git a/conda_forge_webservices/feedstocks_service.py b/conda_forge_webservices/feedstocks_service.py index 277b428fc..36b1fdaa6 100644 --- a/conda_forge_webservices/feedstocks_service.py +++ b/conda_forge_webservices/feedstocks_service.py @@ -4,9 +4,11 @@ import tempfile import shutil import logging -import github -from conda_forge_webservices.tokens import get_app_token_for_webservices_only +from conda_forge_webservices.tokens import ( + get_app_token_for_webservices_only, + get_gh_client, +) from conda_forge_webservices.utils import with_action_url LOGGER = logging.getLogger("conda_forge_webservices.feedstocks_service") @@ -43,7 +45,7 @@ def update_feedstock(org_name, repo_name): # sometimes the webhook outpaces other bits of the API so we try a bit for i in range(5): try: - gh = github.Github(gh_token) + gh = get_gh_client() default_branch = gh.get_repo(f"{org_name}/{repo_name}").default_branch break except Exception as e: diff --git a/conda_forge_webservices/linting.py b/conda_forge_webservices/linting.py index ca5bca0fb..524cb867d 100644 --- a/conda_forge_webservices/linting.py +++ b/conda_forge_webservices/linting.py @@ -7,12 +7,17 @@ from typing import TypedDict from git import GitCommandError, Repo -import github import conda_smithy.lint_recipe -from conda_forge_webservices.tokens import get_app_token_for_webservices_only +from conda_forge_webservices.tokens import get_gh_client LOGGER = logging.getLogger("conda_forge_webservices.linting") +SKIP_MSGS = [ + "[ci skip]", + "[skip ci]", + "[lint skip]", + "[skip lint]", +] class LintInfo(TypedDict): @@ -21,6 +26,30 @@ class LintInfo(TypedDict): sha: str +def lint_via_github_actions(full_name: str, pr_num: int) -> bool: + gh = get_gh_client() + repo = gh.get_repo(full_name) + repo_owner, repo_name = full_name.split("/") + pr = repo.get_pull(pr_num) + sha = pr.head.sha + commit = gh.get_repo(pr.head.repo.full_name).get_git_commit(sha) + commit_msg = commit.message + + should_skip = any([msg in commit_msg for msg in SKIP_MSGS]) + if should_skip: + return False + + running = repo.create_repository_dispatch( + "lint", + client_payload={"pr": pr_num}, + ) + + if running: + _set_pr_status(repo_owner, repo_name, sha, "pending") + + return running + + def find_recipes(path: Path) -> list[Path]: """ Returns all `meta.yaml` and `recipe.yaml` files in the given path. @@ -155,7 +184,7 @@ def _set_pr_status( else: kwargs = {} - gh = github.Github(get_app_token_for_webservices_only()) + gh = get_gh_client() user = gh.get_user(owner) repo = user.get_repo(repo_name) commit = repo.get_commit(sha) @@ -174,7 +203,7 @@ def compute_lint_message( ignore_base: bool = False, set_pending_status: bool = True, ) -> LintInfo | None: - gh = github.Github(get_app_token_for_webservices_only()) + gh = get_gh_client() owner = gh.get_user(repo_owner) remote_repo = owner.get_repo(repo_name) @@ -214,14 +243,8 @@ def compute_lint_message( sha = str(ref_head.commit.hexsha) # Check if the linter is skipped via the commit message. - skip_msgs = [ - "[ci skip]", - "[skip ci]", - "[lint skip]", - "[skip lint]", - ] commit_msg = repo.commit(sha).message - should_skip = any([msg in commit_msg for msg in skip_msgs]) + should_skip = any([msg in commit_msg for msg in SKIP_MSGS]) if should_skip: return None @@ -285,7 +308,7 @@ def comment_on_pr( force: bool = False, search: str | None = None, ): - gh = github.Github(get_app_token_for_webservices_only()) + gh = get_gh_client() user = gh.get_user(owner) repo = user.get_repo(repo_name) @@ -324,7 +347,7 @@ def comment_on_pr( def set_pr_status( owner: str, repo_name: str, lint_info: LintInfo, target_url: str | None = None ): - gh = github.Github(get_app_token_for_webservices_only()) + gh = get_gh_client() user = gh.get_user(owner) repo = user.get_repo(repo_name) diff --git a/conda_forge_webservices/tokens.py b/conda_forge_webservices/tokens.py index d63919cb1..5b63ea2e8 100644 --- a/conda_forge_webservices/tokens.py +++ b/conda_forge_webservices/tokens.py @@ -5,6 +5,7 @@ import sys import logging from contextlib import redirect_stdout, redirect_stderr +from functools import lru_cache from typing import Any @@ -23,6 +24,15 @@ APP_TOKEN_RESET_TIME = None +@lru_cache(maxsize=1) +def _get_gh_client(token): + return Github(auth=Auth.Token(token)) + + +def get_gh_client(): + return _get_gh_client(get_app_token_for_webservices_only()) + + def get_app_token_for_webservices_only(): """Get's an app token that should only be used in the webservices bot. @@ -221,7 +231,7 @@ def _inject_app_token_into_feedstock(full_name, repo=None, readonly=False): ) if token is not None: if repo is None: - gh = Github(get_app_token_for_webservices_only()) + gh = get_gh_client() repo = gh.get_repo(full_name) try: repo.create_secret(token_name, token) diff --git a/conda_forge_webservices/update_teams.py b/conda_forge_webservices/update_teams.py index 088174fd8..225ec7790 100644 --- a/conda_forge_webservices/update_teams.py +++ b/conda_forge_webservices/update_teams.py @@ -7,7 +7,7 @@ from functools import cache from ruamel.yaml import YAML -from conda_forge_webservices.tokens import get_app_token_for_webservices_only +from conda_forge_webservices.tokens import get_gh_client LOGGER = logging.getLogger("conda_forge_webservices.update_teams") @@ -66,7 +66,7 @@ def update_team(org_name, repo_name, commit=None): ] or team_name.startswith("help-"): return - gh = github.Github(get_app_token_for_webservices_only()) + gh = get_gh_client() org = gh.get_organization(org_name) gh_repo = org.get_repo(repo_name) diff --git a/conda_forge_webservices/webapp.py b/conda_forge_webservices/webapp.py index 2b2911615..f79d07923 100644 --- a/conda_forge_webservices/webapp.py +++ b/conda_forge_webservices/webapp.py @@ -189,28 +189,34 @@ async def post(self): LOGGER.info("linting: %s", body["repository"]["full_name"]) LOGGER.info("===================================================") - lint_info = await tornado.ioloop.IOLoop.current().run_in_executor( - _worker_pool(), - linting.compute_lint_message, - owner, - repo_name, - pr_id, - repo_name == "staged-recipes", - ) - if lint_info: - msg = linting.comment_on_pr( - owner, - repo_name, + if True: + linting.lint_via_github_actions( + body["repository"]["full_name"], pr_id, - lint_info["message"], - search="conda-forge-linting service", ) - linting.set_pr_status( + else: + lint_info = await tornado.ioloop.IOLoop.current().run_in_executor( + _worker_pool(), + linting.compute_lint_message, owner, repo_name, - lint_info, - target_url=msg.html_url, + pr_id, + repo_name == "staged-recipes", ) + if lint_info: + msg = linting.comment_on_pr( + owner, + repo_name, + pr_id, + lint_info["message"], + search="conda-forge-linting service", + ) + linting.set_pr_status( + owner, + repo_name, + lint_info, + target_url=msg.html_url, + ) print_rate_limiting_info() else: LOGGER.info(f'Unhandled event "{event}".') From dc7bc58ab26f25fc7403d420032dfe30f91d64e2 Mon Sep 17 00:00:00 2001 From: beckermr Date: Sat, 21 Sep 2024 08:37:45 -0400 Subject: [PATCH 2/7] test: fix the tests for new methods --- .../tests/linting/test_compute_lint_message.py | 12 ++++++------ conda_forge_webservices/tests/test_commands.py | 6 +++--- conda_forge_webservices/tests/test_webapp.py | 3 +++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/conda_forge_webservices/tests/linting/test_compute_lint_message.py b/conda_forge_webservices/tests/linting/test_compute_lint_message.py index 07d18deb1..914143306 100644 --- a/conda_forge_webservices/tests/linting/test_compute_lint_message.py +++ b/conda_forge_webservices/tests/linting/test_compute_lint_message.py @@ -66,21 +66,21 @@ def test_ok_recipe_above_good_recipe(): lint = compute_lint_message( "conda-forge", "conda-forge-webservices", 54, set_pending_status=False ) - assert expected_message == lint["message"] + assert lint["message"].startswith(expected_message) def test_ok_recipe_beside_good_recipe(): expected_message = textwrap.dedent(""" Hi! This is the friendly automated conda-forge-linting service. - I just wanted to let you know that I linted all conda-recipes in your PR (```recipe/meta.yaml```, ```recipes/recipe/meta.yaml```) and found it was in an excellent condition. + I just wanted to let you know that I linted all conda-recipes in your PR (```recipe/blah/meta.yaml```, ```recipe/meta.yaml```, ```recipes/recipe/meta.yaml```) and found it was in an excellent condition. """) # noqa lint = compute_lint_message( "conda-forge", "conda-forge-webservices", 62, set_pending_status=False ) - assert expected_message == lint["message"] + assert lint["message"].startswith(expected_message) def test_ok_recipe_above_ignored_good_recipe(): @@ -94,21 +94,21 @@ def test_ok_recipe_above_ignored_good_recipe(): lint = compute_lint_message( "conda-forge", "conda-forge-webservices", 54, True, set_pending_status=False ) - assert expected_message == lint["message"] + assert lint["message"].startswith(expected_message) def test_ok_recipe_beside_ignored_good_recipe(): expected_message = textwrap.dedent(""" Hi! This is the friendly automated conda-forge-linting service. - I just wanted to let you know that I linted all conda-recipes in your PR (```recipe/meta.yaml```) and found it was in an excellent condition. + I just wanted to let you know that I linted all conda-recipes in your PR (```recipe/blah/meta.yaml```, ```recipe/meta.yaml```) and found it was in an excellent condition. """) # noqa lint = compute_lint_message( "conda-forge", "conda-forge-webservices", 62, True, set_pending_status=False ) - assert expected_message == lint["message"] + assert lint["message"].startswith(expected_message) def test_conflict_ok_recipe(): diff --git a/conda_forge_webservices/tests/test_commands.py b/conda_forge_webservices/tests/test_commands.py index efc1db23e..648a00785 100644 --- a/conda_forge_webservices/tests/test_commands.py +++ b/conda_forge_webservices/tests/test_commands.py @@ -51,7 +51,7 @@ def tearDown(self): @mock.patch("conda_forge_webservices.commands.make_noarch") @mock.patch("conda_forge_webservices.commands.relint") @mock.patch("conda_forge_webservices.commands.update_team") - @mock.patch("github.Github") + @mock.patch("conda_forge.webservices.commands.get_gh_client") @mock.patch("conda_forge_webservices.commands.Repo") def test_pr_command_triggers( self, repo, gh, update_team, relint, make_noarch, rerender, add_bot_rerun_label @@ -171,7 +171,7 @@ def test_pr_command_triggers( @mock.patch("conda_forge_webservices.commands.make_noarch") @mock.patch("conda_forge_webservices.commands.relint") @mock.patch("conda_forge_webservices.commands.update_team") - @mock.patch("github.Github") + @mock.patch("conda_forge.webservices.commands.get_gh_client") @mock.patch("conda_forge_webservices.commands.Repo") def test_issue_command_triggers( self, @@ -367,7 +367,7 @@ def test_issue_command_triggers( @mock.patch("conda_forge_webservices.commands.make_noarch") @mock.patch("conda_forge_webservices.commands.relint") @mock.patch("conda_forge_webservices.commands.update_team") - @mock.patch("github.Github") + @mock.patch("conda_forge.webservices.commands.get_gh_client") @mock.patch("conda_forge_webservices.commands.Repo") def test_rerender_failure( self, repo, gh, update_team, relint, make_noarch, rerender diff --git a/conda_forge_webservices/tests/test_webapp.py b/conda_forge_webservices/tests/test_webapp.py index 47fb8c2a7..743dfcf9c 100644 --- a/conda_forge_webservices/tests/test_webapp.py +++ b/conda_forge_webservices/tests/test_webapp.py @@ -107,6 +107,9 @@ def test_good_header(self, set_pr_status, comment_on_pr, compute_lint_message): target_url=mock.sentinel.html_url, ) + @mock.patch( + "conda_forge_webservices.linting.lint_via_github_actions", return_value=None + ) @mock.patch( "conda_forge_webservices.linting.compute_lint_message", return_value=None ) From 092763497224319b5adc37ae89ad6b46165a3e80 Mon Sep 17 00:00:00 2001 From: beckermr Date: Sat, 21 Sep 2024 08:44:08 -0400 Subject: [PATCH 3/7] fix: wrong patch path --- conda_forge_webservices/tests/test_commands.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conda_forge_webservices/tests/test_commands.py b/conda_forge_webservices/tests/test_commands.py index 648a00785..269822b9c 100644 --- a/conda_forge_webservices/tests/test_commands.py +++ b/conda_forge_webservices/tests/test_commands.py @@ -51,7 +51,7 @@ def tearDown(self): @mock.patch("conda_forge_webservices.commands.make_noarch") @mock.patch("conda_forge_webservices.commands.relint") @mock.patch("conda_forge_webservices.commands.update_team") - @mock.patch("conda_forge.webservices.commands.get_gh_client") + @mock.patch("conda_forge_webservices.commands.get_gh_client") @mock.patch("conda_forge_webservices.commands.Repo") def test_pr_command_triggers( self, repo, gh, update_team, relint, make_noarch, rerender, add_bot_rerun_label @@ -171,7 +171,7 @@ def test_pr_command_triggers( @mock.patch("conda_forge_webservices.commands.make_noarch") @mock.patch("conda_forge_webservices.commands.relint") @mock.patch("conda_forge_webservices.commands.update_team") - @mock.patch("conda_forge.webservices.commands.get_gh_client") + @mock.patch("conda_forge_webservices.commands.get_gh_client") @mock.patch("conda_forge_webservices.commands.Repo") def test_issue_command_triggers( self, @@ -367,7 +367,7 @@ def test_issue_command_triggers( @mock.patch("conda_forge_webservices.commands.make_noarch") @mock.patch("conda_forge_webservices.commands.relint") @mock.patch("conda_forge_webservices.commands.update_team") - @mock.patch("conda_forge.webservices.commands.get_gh_client") + @mock.patch("conda_forge_webservices.commands.get_gh_client") @mock.patch("conda_forge_webservices.commands.Repo") def test_rerender_failure( self, repo, gh, update_team, relint, make_noarch, rerender From f88af5f6427f1700788286c7bd4c9badf7d367a5 Mon Sep 17 00:00:00 2001 From: beckermr Date: Sat, 21 Sep 2024 08:57:04 -0400 Subject: [PATCH 4/7] test: fix the tests for new methods --- conda_forge_webservices/tests/test_commands.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conda_forge_webservices/tests/test_commands.py b/conda_forge_webservices/tests/test_commands.py index 269822b9c..ffa1d5c53 100644 --- a/conda_forge_webservices/tests/test_commands.py +++ b/conda_forge_webservices/tests/test_commands.py @@ -171,11 +171,13 @@ def test_pr_command_triggers( @mock.patch("conda_forge_webservices.commands.make_noarch") @mock.patch("conda_forge_webservices.commands.relint") @mock.patch("conda_forge_webservices.commands.update_team") + @mock.patch("conda_forge_webservices.commands.github.Github") @mock.patch("conda_forge_webservices.commands.get_gh_client") @mock.patch("conda_forge_webservices.commands.Repo") def test_issue_command_triggers( self, git_repo, + gh_app, gh, update_team, relint, From aea9b0e32ba30f6b2ca2cb62813232d4c013c07b Mon Sep 17 00:00:00 2001 From: beckermr Date: Sat, 21 Sep 2024 09:04:26 -0400 Subject: [PATCH 5/7] test: fix the tests for new methods --- conda_forge_webservices/tests/test_commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda_forge_webservices/tests/test_commands.py b/conda_forge_webservices/tests/test_commands.py index ffa1d5c53..a733b7540 100644 --- a/conda_forge_webservices/tests/test_commands.py +++ b/conda_forge_webservices/tests/test_commands.py @@ -305,7 +305,7 @@ def test_issue_command_triggers( ] for command, should, should_not in commands: - issue = gh.return_value.get_repo.return_value.get_issue.return_value + issue = gh_app.return_value.get_repo.return_value.get_issue.return_value repo = gh.return_value.get_repo.return_value gh.return_value.get_repo.return_value.default_branch = "main" for msg in should: From 0ad4b14163f567de3a73b69f8116e49078228b96 Mon Sep 17 00:00:00 2001 From: beckermr Date: Sat, 21 Sep 2024 14:38:50 -0400 Subject: [PATCH 6/7] fix: ensure correct mocks called --- conda_forge_webservices/linting.py | 1 + conda_forge_webservices/tests/test_webapp.py | 95 +++++++++++++------- conda_forge_webservices/webapp.py | 2 +- 3 files changed, 63 insertions(+), 35 deletions(-) diff --git a/conda_forge_webservices/linting.py b/conda_forge_webservices/linting.py index 524cb867d..ee9ef602e 100644 --- a/conda_forge_webservices/linting.py +++ b/conda_forge_webservices/linting.py @@ -18,6 +18,7 @@ "[lint skip]", "[skip lint]", ] +LINT_VIA_GHA = True class LintInfo(TypedDict): diff --git a/conda_forge_webservices/tests/test_webapp.py b/conda_forge_webservices/tests/test_webapp.py index 743dfcf9c..bf88d44ae 100644 --- a/conda_forge_webservices/tests/test_webapp.py +++ b/conda_forge_webservices/tests/test_webapp.py @@ -9,6 +9,7 @@ from tornado.testing import AsyncHTTPTestCase from conda_forge_webservices.webapp import create_webapp +from conda_forge_webservices import linting class TestHandlerBase(AsyncHTTPTestCase): @@ -44,6 +45,10 @@ def test_bad_hash(self): ) self.assertIn(response.code, [403, 500]) + @mock.patch( + "conda_forge_webservices.linting.lint_via_github_actions", + return_value=None, + ) @mock.patch( "conda_forge_webservices.linting.compute_lint_message", return_value={"message": mock.sentinel.message}, @@ -53,7 +58,13 @@ def test_bad_hash(self): return_value=mock.MagicMock(html_url=mock.sentinel.html_url), ) @mock.patch("conda_forge_webservices.linting.set_pr_status") - def test_good_header(self, set_pr_status, comment_on_pr, compute_lint_message): + def test_good_header( + self, + set_pr_status, + comment_on_pr, + compute_lint_message, + lint_via_gha, + ): PR_number = 16 body = { "repository": { @@ -88,24 +99,29 @@ def test_good_header(self, set_pr_status, comment_on_pr, compute_lint_message): self.assertEqual(response.code, 200) - compute_lint_message.assert_called_once_with( - "conda-forge", "repo_name-feedstock", PR_number, False - ) + if linting.LINT_VIA_GHA: + lint_via_gha.assert_called_once_with( + "conda-forge", "repo_name-feedstock", PR_number + ) + else: + compute_lint_message.assert_called_once_with( + "conda-forge", "repo_name-feedstock", PR_number, False + ) - comment_on_pr.assert_called_once_with( - "conda-forge", - "repo_name-feedstock", - PR_number, - mock.sentinel.message, - search="conda-forge-linting service", - ) + comment_on_pr.assert_called_once_with( + "conda-forge", + "repo_name-feedstock", + PR_number, + mock.sentinel.message, + search="conda-forge-linting service", + ) - set_pr_status.assert_called_once_with( - "conda-forge", - "repo_name-feedstock", - {"message": mock.sentinel.message}, - target_url=mock.sentinel.html_url, - ) + set_pr_status.assert_called_once_with( + "conda-forge", + "repo_name-feedstock", + {"message": mock.sentinel.message}, + target_url=mock.sentinel.html_url, + ) @mock.patch( "conda_forge_webservices.linting.lint_via_github_actions", return_value=None @@ -326,6 +342,10 @@ def test_skip_commits(self, *args): msg=f"event: {event}, slug: {slug}, hook: {hook}", ) + @mock.patch( + "conda_forge_webservices.linting.lint_via_github_actions", + return_value=None, + ) @mock.patch( "conda_forge_webservices.linting.compute_lint_message", return_value={"message": mock.sentinel.message}, @@ -335,7 +355,9 @@ def test_skip_commits(self, *args): return_value=mock.MagicMock(html_url=mock.sentinel.html_url), ) @mock.patch("conda_forge_webservices.linting.set_pr_status") - def test_staged_recipes(self, set_pr_status, comment_on_pr, compute_lint_message): + def test_staged_recipes( + self, set_pr_status, comment_on_pr, compute_lint_message, lint_via_gha + ): PR_number = 16 body = { "repository": { @@ -369,24 +391,29 @@ def test_staged_recipes(self, set_pr_status, comment_on_pr, compute_lint_message ) self.assertEqual(response.code, 200) - compute_lint_message.assert_called_once_with( - "conda-forge", "staged-recipes", PR_number, True - ) + if linting.LINT_VIA_GHA: + lint_via_gha.assert_called_once_with( + "conda-forge", "staged-recipes", PR_number + ) + else: + compute_lint_message.assert_called_once_with( + "conda-forge", "staged-recipes", PR_number, True + ) - comment_on_pr.assert_called_once_with( - "conda-forge", - "staged-recipes", - PR_number, - mock.sentinel.message, - search="conda-forge-linting service", - ) + comment_on_pr.assert_called_once_with( + "conda-forge", + "staged-recipes", + PR_number, + mock.sentinel.message, + search="conda-forge-linting service", + ) - set_pr_status.assert_called_once_with( - "conda-forge", - "staged-recipes", - {"message": mock.sentinel.message}, - target_url=mock.sentinel.html_url, - ) + set_pr_status.assert_called_once_with( + "conda-forge", + "staged-recipes", + {"message": mock.sentinel.message}, + target_url=mock.sentinel.html_url, + ) @mock.patch( "conda_forge_webservices.linting.compute_lint_message", diff --git a/conda_forge_webservices/webapp.py b/conda_forge_webservices/webapp.py index f79d07923..8cc86eebe 100644 --- a/conda_forge_webservices/webapp.py +++ b/conda_forge_webservices/webapp.py @@ -189,7 +189,7 @@ async def post(self): LOGGER.info("linting: %s", body["repository"]["full_name"]) LOGGER.info("===================================================") - if True: + if linting.LINT_VIA_GHA: linting.lint_via_github_actions( body["repository"]["full_name"], pr_id, From f89a061a9169fef5ee460915763929471dfd0772 Mon Sep 17 00:00:00 2001 From: beckermr Date: Sat, 21 Sep 2024 14:43:18 -0400 Subject: [PATCH 7/7] fix: wrong call --- conda_forge_webservices/tests/test_webapp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conda_forge_webservices/tests/test_webapp.py b/conda_forge_webservices/tests/test_webapp.py index bf88d44ae..314fa0ac8 100644 --- a/conda_forge_webservices/tests/test_webapp.py +++ b/conda_forge_webservices/tests/test_webapp.py @@ -101,7 +101,7 @@ def test_good_header( if linting.LINT_VIA_GHA: lint_via_gha.assert_called_once_with( - "conda-forge", "repo_name-feedstock", PR_number + "conda-forge/repo_name-feedstock", PR_number ) else: compute_lint_message.assert_called_once_with( @@ -393,7 +393,7 @@ def test_staged_recipes( self.assertEqual(response.code, 200) if linting.LINT_VIA_GHA: lint_via_gha.assert_called_once_with( - "conda-forge", "staged-recipes", PR_number + "conda-forge/staged-recipes", PR_number ) else: compute_lint_message.assert_called_once_with(