2021-09-15 12:59:39 +00:00
|
|
|
#!/usr/bin/env python3
|
2021-09-15 13:04:29 +00:00
|
|
|
import logging
|
2024-01-31 16:59:13 +00:00
|
|
|
import sys
|
2022-02-03 12:27:32 +00:00
|
|
|
from typing import Tuple
|
2021-11-26 14:00:09 +00:00
|
|
|
|
2024-01-31 16:59:13 +00:00
|
|
|
# isort: off
|
2022-02-03 12:27:32 +00:00
|
|
|
from github import Github
|
2022-04-14 11:01:25 +00:00
|
|
|
|
2024-01-31 16:59:13 +00:00
|
|
|
# isort: on
|
|
|
|
|
2024-04-17 20:23:41 +00:00
|
|
|
from cherry_pick import Labels
|
2022-07-18 19:15:21 +00:00
|
|
|
from commit_status_helper import (
|
2023-04-20 11:55:33 +00:00
|
|
|
CI_STATUS_NAME,
|
2023-04-25 15:02:41 +00:00
|
|
|
create_ci_report,
|
2023-02-23 14:21:19 +00:00
|
|
|
format_description,
|
2022-07-18 19:15:21 +00:00
|
|
|
get_commit,
|
2023-04-06 09:40:32 +00:00
|
|
|
post_commit_status,
|
2022-07-18 19:15:21 +00:00
|
|
|
post_labels,
|
|
|
|
remove_labels,
|
|
|
|
)
|
2023-04-24 13:41:43 +00:00
|
|
|
from env_helper import GITHUB_REPOSITORY, GITHUB_SERVER_URL
|
2021-10-20 11:25:14 +00:00
|
|
|
from get_robot_token import get_best_robot_token
|
2023-05-23 16:47:19 +00:00
|
|
|
from lambda_shared_package.lambda_shared.pr import (
|
|
|
|
CATEGORY_TO_LABEL,
|
|
|
|
TRUSTED_CONTRIBUTORS,
|
|
|
|
check_pr_description,
|
|
|
|
)
|
2024-01-29 17:13:32 +00:00
|
|
|
from pr_info import PRInfo
|
2024-02-28 10:33:32 +00:00
|
|
|
from report import FAILURE, PENDING, SUCCESS
|
2021-09-15 12:59:39 +00:00
|
|
|
|
|
|
|
TRUSTED_ORG_IDS = {
|
|
|
|
54801242, # clickhouse
|
|
|
|
}
|
|
|
|
|
2022-02-05 17:26:43 +00:00
|
|
|
OK_SKIP_LABELS = {"release", "pr-backport", "pr-cherrypick"}
|
2022-02-03 12:27:32 +00:00
|
|
|
CAN_BE_TESTED_LABEL = "can be tested"
|
2023-04-24 08:39:49 +00:00
|
|
|
FEATURE_LABEL = "pr-feature"
|
2022-03-29 17:28:18 +00:00
|
|
|
SUBMODULE_CHANGED_LABEL = "submodule changed"
|
2023-12-21 10:53:01 +00:00
|
|
|
PR_CHECK = "PR Check"
|
2024-02-28 19:30:34 +00:00
|
|
|
# pr-bugfix autoport can lead to issues in releases, let's do ci fixes only
|
|
|
|
AUTO_BACKPORT_LABELS = ["pr-ci"]
|
2021-09-15 12:59:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
def pr_is_by_trusted_user(pr_user_login, pr_user_orgs):
|
2021-12-13 14:34:17 +00:00
|
|
|
if pr_user_login.lower() in TRUSTED_CONTRIBUTORS:
|
2021-10-27 07:03:24 +00:00
|
|
|
logging.info("User '%s' is trusted", pr_user_login)
|
2021-09-15 12:59:39 +00:00
|
|
|
return True
|
|
|
|
|
2021-10-27 07:03:24 +00:00
|
|
|
logging.info("User '%s' is not trusted", pr_user_login)
|
2021-09-15 12:59:39 +00:00
|
|
|
|
|
|
|
for org_id in pr_user_orgs:
|
|
|
|
if org_id in TRUSTED_ORG_IDS:
|
2022-01-13 11:06:50 +00:00
|
|
|
logging.info(
|
|
|
|
"Org '%s' is trusted; will mark user %s as trusted",
|
|
|
|
org_id,
|
|
|
|
pr_user_login,
|
|
|
|
)
|
2021-09-15 12:59:39 +00:00
|
|
|
return True
|
2021-10-27 07:03:24 +00:00
|
|
|
logging.info("Org '%s' is not trusted", org_id)
|
2021-09-15 12:59:39 +00:00
|
|
|
|
|
|
|
return False
|
|
|
|
|
2022-01-13 11:06:50 +00:00
|
|
|
|
2021-09-15 12:59:39 +00:00
|
|
|
# Returns whether we should look into individual checks for this PR. If not, it
|
|
|
|
# can be skipped entirely.
|
2023-12-21 10:53:01 +00:00
|
|
|
# Returns can_run, description
|
|
|
|
def should_run_ci_for_pr(pr_info: PRInfo) -> Tuple[bool, str]:
|
2021-09-15 12:59:39 +00:00
|
|
|
# Consider the labels and whether the user is trusted.
|
2021-12-22 08:13:04 +00:00
|
|
|
print("Got labels", pr_info.labels)
|
2021-09-15 12:59:39 +00:00
|
|
|
|
2023-03-20 12:45:23 +00:00
|
|
|
if OK_SKIP_LABELS.intersection(pr_info.labels):
|
2023-12-21 10:53:01 +00:00
|
|
|
return True, "Don't try new checks for release/backports/cherry-picks"
|
2023-03-20 12:45:23 +00:00
|
|
|
|
2022-02-03 12:27:32 +00:00
|
|
|
if CAN_BE_TESTED_LABEL not in pr_info.labels and not pr_is_by_trusted_user(
|
2022-01-13 11:06:50 +00:00
|
|
|
pr_info.user_login, pr_info.user_orgs
|
|
|
|
):
|
2022-09-07 08:51:02 +00:00
|
|
|
print(
|
|
|
|
f"PRs by untrusted users need the '{CAN_BE_TESTED_LABEL}' label - please contact a member of the core team"
|
|
|
|
)
|
2023-12-21 10:53:01 +00:00
|
|
|
return False, "Needs 'can be tested' label"
|
2021-09-15 12:59:39 +00:00
|
|
|
|
2023-12-21 10:53:01 +00:00
|
|
|
return True, "No special conditions apply"
|
2021-09-15 12:59:39 +00:00
|
|
|
|
2021-11-26 14:00:09 +00:00
|
|
|
|
2023-04-06 09:03:32 +00:00
|
|
|
def main():
|
2021-09-15 12:59:39 +00:00
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
|
2022-03-29 17:28:18 +00:00
|
|
|
pr_info = PRInfo(need_orgs=True, pr_event_from_api=True, need_changed_files=True)
|
2023-03-20 12:45:23 +00:00
|
|
|
# The case for special branches like backports and releases without created
|
|
|
|
# PRs, like merged backport branches that are reset immediately after merge
|
|
|
|
if pr_info.number == 0:
|
|
|
|
print("::notice ::Cannot run, no PR exists for the commit")
|
|
|
|
sys.exit(1)
|
|
|
|
|
2023-12-21 10:53:01 +00:00
|
|
|
can_run, description = should_run_ci_for_pr(pr_info)
|
2023-03-20 12:45:23 +00:00
|
|
|
if can_run and OK_SKIP_LABELS.intersection(pr_info.labels):
|
|
|
|
print("::notice :: Early finish the check, running in a special PR")
|
|
|
|
sys.exit(0)
|
|
|
|
|
2023-02-27 12:11:17 +00:00
|
|
|
description = format_description(description)
|
2022-07-30 05:07:22 +00:00
|
|
|
gh = Github(get_best_robot_token(), per_page=100)
|
2021-10-20 11:25:14 +00:00
|
|
|
commit = get_commit(gh, pr_info.sha)
|
2022-01-13 11:08:31 +00:00
|
|
|
|
2023-08-01 15:06:23 +00:00
|
|
|
description_error, category = check_pr_description(pr_info.body, GITHUB_REPOSITORY)
|
2022-03-30 08:58:34 +00:00
|
|
|
pr_labels_to_add = []
|
|
|
|
pr_labels_to_remove = []
|
2022-03-29 17:50:06 +00:00
|
|
|
if (
|
2022-04-19 12:47:18 +00:00
|
|
|
category in CATEGORY_TO_LABEL
|
|
|
|
and CATEGORY_TO_LABEL[category] not in pr_info.labels
|
2022-03-29 17:50:06 +00:00
|
|
|
):
|
2022-04-19 12:47:18 +00:00
|
|
|
pr_labels_to_add.append(CATEGORY_TO_LABEL[category])
|
2022-03-30 08:58:34 +00:00
|
|
|
|
|
|
|
for label in pr_info.labels:
|
2022-03-30 09:19:11 +00:00
|
|
|
if (
|
2022-04-19 12:47:18 +00:00
|
|
|
label in CATEGORY_TO_LABEL.values()
|
|
|
|
and category in CATEGORY_TO_LABEL
|
|
|
|
and label != CATEGORY_TO_LABEL[category]
|
2022-03-30 09:19:11 +00:00
|
|
|
):
|
2022-03-30 08:58:34 +00:00
|
|
|
pr_labels_to_remove.append(label)
|
2022-03-29 13:48:57 +00:00
|
|
|
|
2022-03-29 17:28:18 +00:00
|
|
|
if pr_info.has_changes_in_submodules():
|
2022-03-30 08:58:34 +00:00
|
|
|
pr_labels_to_add.append(SUBMODULE_CHANGED_LABEL)
|
2022-03-29 17:50:06 +00:00
|
|
|
elif SUBMODULE_CHANGED_LABEL in pr_info.labels:
|
2022-03-30 08:58:34 +00:00
|
|
|
pr_labels_to_remove.append(SUBMODULE_CHANGED_LABEL)
|
|
|
|
|
2024-02-27 16:33:46 +00:00
|
|
|
if any(label in AUTO_BACKPORT_LABELS for label in pr_labels_to_add):
|
|
|
|
backport_labels = [Labels.MUST_BACKPORT, Labels.MUST_BACKPORT_CLOUD]
|
|
|
|
pr_labels_to_add += [
|
|
|
|
label for label in backport_labels if label not in pr_info.labels
|
|
|
|
]
|
|
|
|
print(
|
|
|
|
f"::notice :: Add backport labels [{backport_labels}] for a given PR category"
|
|
|
|
)
|
|
|
|
|
2022-09-07 08:35:54 +00:00
|
|
|
print(f"Change labels: add {pr_labels_to_add}, remove {pr_labels_to_remove}")
|
2022-03-30 08:58:34 +00:00
|
|
|
if pr_labels_to_add:
|
|
|
|
post_labels(gh, pr_info, pr_labels_to_add)
|
|
|
|
|
|
|
|
if pr_labels_to_remove:
|
|
|
|
remove_labels(gh, pr_info, pr_labels_to_remove)
|
2022-03-29 17:28:18 +00:00
|
|
|
|
2022-04-19 12:47:18 +00:00
|
|
|
if description_error:
|
2022-04-04 23:06:46 +00:00
|
|
|
print(
|
|
|
|
"::error ::Cannot run, PR description does not match the template: "
|
2022-04-19 12:47:18 +00:00
|
|
|
f"{description_error}"
|
2022-04-04 23:06:46 +00:00
|
|
|
)
|
2022-01-26 12:23:20 +00:00
|
|
|
logging.info(
|
2023-04-20 11:55:33 +00:00
|
|
|
"PR body doesn't match the template: (start)\n%s\n(end)\nReason: %s",
|
2022-04-04 23:06:46 +00:00
|
|
|
pr_info.body,
|
2022-04-19 12:47:18 +00:00
|
|
|
description_error,
|
2022-01-26 12:23:20 +00:00
|
|
|
)
|
2022-01-13 11:08:31 +00:00
|
|
|
url = (
|
|
|
|
f"{GITHUB_SERVER_URL}/{GITHUB_REPOSITORY}/"
|
|
|
|
"blob/master/.github/PULL_REQUEST_TEMPLATE.md?plain=1"
|
|
|
|
)
|
2023-04-06 09:40:32 +00:00
|
|
|
post_commit_status(
|
2023-04-20 11:55:33 +00:00
|
|
|
commit,
|
2024-01-31 16:59:13 +00:00
|
|
|
FAILURE,
|
2023-04-20 11:55:33 +00:00
|
|
|
url,
|
|
|
|
format_description(description_error),
|
2023-12-21 10:53:01 +00:00
|
|
|
PR_CHECK,
|
2023-04-20 11:55:33 +00:00
|
|
|
pr_info,
|
2022-01-13 11:08:31 +00:00
|
|
|
)
|
|
|
|
sys.exit(1)
|
|
|
|
|
2023-12-21 10:53:01 +00:00
|
|
|
if FEATURE_LABEL in pr_info.labels and not pr_info.has_changes_in_documentation():
|
|
|
|
print(
|
|
|
|
f"The '{FEATURE_LABEL}' in the labels, "
|
|
|
|
"but there's no changed documentation"
|
2023-04-20 11:55:33 +00:00
|
|
|
)
|
2023-04-24 13:41:43 +00:00
|
|
|
post_commit_status(
|
2023-12-18 08:07:22 +00:00
|
|
|
commit,
|
2023-12-21 10:53:01 +00:00
|
|
|
FAILURE,
|
|
|
|
"",
|
|
|
|
f"expect adding docs for {FEATURE_LABEL}",
|
|
|
|
PR_CHECK,
|
2023-12-18 08:07:22 +00:00
|
|
|
pr_info,
|
2023-04-24 13:41:43 +00:00
|
|
|
)
|
2023-12-21 10:53:01 +00:00
|
|
|
# allow the workflow to continue
|
|
|
|
|
|
|
|
if not can_run:
|
2024-01-31 16:59:13 +00:00
|
|
|
post_commit_status(
|
|
|
|
commit,
|
|
|
|
FAILURE,
|
|
|
|
"",
|
|
|
|
description,
|
|
|
|
PR_CHECK,
|
|
|
|
pr_info,
|
|
|
|
)
|
2023-12-21 10:53:01 +00:00
|
|
|
print("::notice ::Cannot run")
|
|
|
|
sys.exit(1)
|
2024-02-28 12:37:07 +00:00
|
|
|
|
2024-02-28 11:22:19 +00:00
|
|
|
post_commit_status(
|
|
|
|
commit,
|
|
|
|
SUCCESS,
|
|
|
|
"",
|
|
|
|
"ok",
|
|
|
|
PR_CHECK,
|
|
|
|
pr_info,
|
|
|
|
)
|
2023-12-21 10:53:01 +00:00
|
|
|
|
|
|
|
ci_report_url = create_ci_report(pr_info, [])
|
|
|
|
print("::notice ::Can run")
|
2024-04-08 16:09:47 +00:00
|
|
|
|
2024-04-17 20:23:41 +00:00
|
|
|
if not pr_info.is_merge_queue:
|
2024-04-08 16:09:47 +00:00
|
|
|
# we need clean CI status for MQ to merge (no pending statuses)
|
|
|
|
post_commit_status(
|
|
|
|
commit,
|
|
|
|
PENDING,
|
|
|
|
ci_report_url,
|
|
|
|
description,
|
|
|
|
CI_STATUS_NAME,
|
|
|
|
pr_info,
|
|
|
|
)
|
2023-04-06 09:03:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|