Skip to content

Commit

Permalink
Merge pull request #683 from conda-forge/revert-682-revert-681-revert…
Browse files Browse the repository at this point in the history
…-680-revert-679-lint-gha

feat: move linting to GHA
  • Loading branch information
beckermr authored Sep 23, 2024
2 parents 49a66cf + a73c456 commit 82e64ae
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 105 deletions.
65 changes: 38 additions & 27 deletions conda_forge_webservices/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,18 @@
import logging

# from .utils import tmp_directory
from .linting import compute_lint_message, comment_on_pr, set_pr_status
from .linting import (
compute_lint_message,
comment_on_pr,
set_pr_status,
lint_via_github_actions,
LINT_VIA_GHA,
)
from .update_teams import update_team
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,
)
Expand Down Expand Up @@ -148,7 +155,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(
Expand Down Expand Up @@ -178,10 +185,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":
Expand All @@ -198,7 +203,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)
Expand All @@ -219,7 +224,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)
Expand All @@ -232,7 +237,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)
Expand All @@ -252,16 +257,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):
Expand Down Expand Up @@ -342,7 +348,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)
Expand Down Expand Up @@ -385,8 +391,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:
Expand All @@ -405,7 +409,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:
Expand Down Expand Up @@ -458,11 +462,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:
Expand Down Expand Up @@ -971,7 +976,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)
Expand All @@ -984,7 +989,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)
Expand Down Expand Up @@ -1023,17 +1028,23 @@ def make_noarch(repo):

def relint(owner, repo_name, pr_num):
pr = int(pr_num)
lint_info = compute_lint_message(
owner,
repo_name,
pr,
repo_name == "staged-recipes",
)
if not lint_info:
LOGGER.warning("Linting was skipped.")
if LINT_VIA_GHA:
lint_via_github_actions(
f"{owner}/{repo_name}",
pr,
)
else:
msg = comment_on_pr(owner, repo_name, pr, lint_info["message"], force=True)
set_pr_status(owner, repo_name, lint_info, target_url=msg.html_url)
lint_info = compute_lint_message(
owner,
repo_name,
pr,
repo_name == "staged-recipes",
)
if not lint_info:
LOGGER.warning("Linting was skipped.")
else:
msg = comment_on_pr(owner, repo_name, pr, lint_info["message"], force=True)
set_pr_status(owner, repo_name, lint_info, target_url=msg.html_url)


def add_bot_rerun_label(repo, pr_num):
Expand Down
10 changes: 6 additions & 4 deletions conda_forge_webservices/feedstock_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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")]

Expand Down
8 changes: 5 additions & 3 deletions conda_forge_webservices/feedstocks_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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:
Expand Down
50 changes: 37 additions & 13 deletions conda_forge_webservices/linting.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@
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]",
]
LINT_VIA_GHA = True


class LintInfo(TypedDict):
Expand All @@ -21,6 +27,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.
Expand Down Expand Up @@ -143,7 +173,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)
Expand All @@ -162,7 +192,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)
Expand Down Expand Up @@ -202,14 +232,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

Expand Down Expand Up @@ -273,7 +297,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)
Expand Down Expand Up @@ -312,7 +336,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)
Expand Down
12 changes: 8 additions & 4 deletions conda_forge_webservices/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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("github.Github")
@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,
Expand Down Expand Up @@ -303,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:
Expand Down Expand Up @@ -367,7 +369,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
Expand Down Expand Up @@ -396,12 +398,14 @@ def test_rerender_failure(
@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("github.Github")
@mock.patch("conda_forge_webservices.commands.Repo")
def test_update_version_failure(
self,
repo,
gh,
gh_app,
update_team,
relint,
make_noarch,
Expand Down
Loading

0 comments on commit 82e64ae

Please sign in to comment.