Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a comment when downloading and merging from a PR fails via comment_download_pr() #248

Merged
merged 16 commits into from
Feb 14, 2024
Merged
10 changes: 10 additions & 0 deletions app.cfg.example
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,13 @@ no_matching_tarball = No tarball matching `{tarball_pattern}` found in job dir.
multiple_tarballs = Found {num_tarballs} tarballs in job dir - only 1 matching `{tarball_pattern}` expected.
job_result_unknown_fmt = <details><summary>:shrug: UNKNOWN _(click triangle for detailed information)_</summary><ul><li>Job results file `{filename}` does not exist in job directory, or parsing it failed.</li><li>No artefacts were found/reported.</li></ul></details>
job_test_unknown_fmt = <details><summary>:shrug: UNKNOWN _(click triangle for detailed information)_</summary><ul><li>Job test file `{filename}` does not exist in job directory, or parsing it failed.</li></ul></details>

[download_pr_comments]
git_clone_failure = Unable to clone the target repository.
git_clone_tip = _Tip: This could be a connection failure. Try again and if the issue remains check if the address is correct_.
git_checkout_failure = Unable to checkout to the correct branch.
git_checkout_tip = _Tip: Ensure that the branch name is correct and the target branch is available._
curl_failure = Unable to download the `.diff` file.
curl_tip = _Tip: This could be a connection failure. Try again and if the issue remains check if the address is correct_
git_apply_failure = Unable to download or merge changes between the source branch and the destination branch.
git_apply_tip = _Tip: This can usually be resolved by syncing your branch and resolving any merge conflicts._
104 changes: 96 additions & 8 deletions tasks/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,22 @@
BUILD_PERMISSION = "build_permission"
CFG_DIRNAME = "cfg"
CONTAINER_CACHEDIR = "container_cachedir"
CURL_FAILURE = "curl_failure"
CURL_TIP = "curl_tip"
CVMFS_CUSTOMIZATIONS = "cvmfs_customizations"
DEFAULT_JOB_TIME_LIMIT = "24:00:00"
DOWNLOAD_PR_COMMENTS = "download_pr_comments"
ERROR_CURL = "curl"
ERROR_GIT_APPLY = "git apply"
ERROR_GIT_CHECKOUT = "git checkout"
ERROR_GIT_CLONE = "curl"
GITHUB = "github"
GIT_CLONE_FAILURE = "git_clone_failure"
GIT_CLONE_TIP = "git_clone_tip"
GIT_CHECKOUT_FAILURE = "git_checkout_failure"
GIT_CHECKOUT_TIP = "git_checkout_tip"
GIT_APPLY_FAILURE = "git_apply_failure"
GIT_APPLY_TIP = "git_apply_tip"
HTTPS_PROXY = "https_proxy"
HTTP_PROXY = "http_proxy"
INITIAL_COMMENT = "initial_comment"
Expand Down Expand Up @@ -337,7 +350,9 @@ def download_pr(repo_name, branch_name, pr, arch_job_dir):
arch_job_dir (string): working directory of the job to be submitted

Returns:
None (implicitly)
None (implicitly), in case an error is caught in the git clone, git checkout, curl,
or git apply commands, returns the output, stderror, exit code and a string
stating which of these commands failed.
"""
# download pull request to arch_job_dir
# - 'git clone' repository into arch_job_dir (NOTE 'git clone' requires that
Expand All @@ -346,20 +361,91 @@ def download_pr(repo_name, branch_name, pr, arch_job_dir):
# - 'curl' diff for pull request
# - 'git apply' diff file
git_clone_cmd = ' '.join(['git clone', f'https://github.com/{repo_name}', arch_job_dir])
clone_output, clone_error, clone_exit_code = run_cmd(git_clone_cmd, "Clone repo", arch_job_dir)
log(f'cloning with command {git_clone_cmd}')
clone_output, clone_error, clone_exit_code = run_cmd(
git_clone_cmd, "Clone repo", arch_job_dir, raise_on_error=False
)
if clone_exit_code != 0:
error_stage = ERROR_GIT_CLONE
return clone_output, clone_error, clone_exit_code, error_stage

git_checkout_cmd = ' '.join([
'git checkout',
branch_name,
])
checkout_output, checkout_err, checkout_exit_code = run_cmd(git_checkout_cmd,
"checkout branch '%s'" % branch_name, arch_job_dir)
log(f'checking out with command {git_checkout_cmd}')
checkout_output, checkout_err, checkout_exit_code = run_cmd(
git_checkout_cmd, "checkout branch '%s'" % branch_name, arch_job_dir, raise_on_error=False
)
if checkout_exit_code != 0:
error_stage = ERROR_GIT_CHECKOUT
return checkout_output, checkout_err, checkout_exit_code, error_stage

curl_cmd = f'curl -L https://github.com/{repo_name}/pull/{pr.number}.diff > {pr.number}.diff'
curl_output, curl_error, curl_exit_code = run_cmd(curl_cmd, "Obtain patch", arch_job_dir)
log(f'curl with command {curl_cmd}')
curl_output, curl_error, curl_exit_code = run_cmd(
curl_cmd, "Obtain patch", arch_job_dir, raise_on_error=False
)
if curl_exit_code != 0:
error_stage = ERROR_CURL
return curl_output, curl_error, curl_exit_code, error_stage

git_apply_cmd = f'git apply {pr.number}.diff'
git_apply_output, git_apply_error, git_apply_exit_code = run_cmd(git_apply_cmd, "Apply patch", arch_job_dir)
log(f'git apply with command {git_apply_cmd}')
git_apply_output, git_apply_error, git_apply_exit_code = run_cmd(
git_apply_cmd, "Apply patch", arch_job_dir, raise_on_error=False
)
if git_apply_exit_code != 0:
error_stage = ERROR_GIT_APPLY
return git_apply_output, git_apply_error, git_apply_exit_code, error_stage


def comment_download_pr(base_repo_name, pr, download_pr_exit_code, download_pr_error, error_stage):
"""
Handle download_pr() exit code and write helpful comment to PR in case of failure

Args:
base_repo_name (string): name of the repository (format USER_OR_ORGANISATION/REPOSITORY)
pr (github.PullRequest.PullRequest): instance representing the pull request
download_pr_exit_code (int): exit code from download_pr(). 0 if all tasks were successful,
otherwise it corresponds to the error codes of git clone, git checkout, git apply, or curl.
download_pr_error (string): none, or the output of stderr from git clone, git checkout, git apply or curl.
error_stage (string): a string informing the stage where download_pr() failed. Can be 'git clone',
'git checkout', 'curl', or 'git apply'.

Return:
None (implicitly). A comment is created in the appropriate PR.

"""
if download_pr_exit_code != 0:
fn = sys._getframe().f_code.co_name

download_pr_comments_cfg = config.read_config()[DOWNLOAD_PR_COMMENTS]
if error_stage == ERROR_GIT_CLONE:
download_comment = (f"`{download_pr_error}`"
f"{download_pr_comments_cfg[GIT_CLONE_FAILURE]}"
f"{download_pr_comments_cfg[GIT_CLONE_TIP]}")
elif error_stage == ERROR_GIT_CHECKOUT:
download_comment = (f"`{download_pr_error}`"
f"{download_pr_comments_cfg[GIT_CHECKOUT_FAILURE]}"
f"{download_pr_comments_cfg[GIT_CHECKOUT_TIP]}")
elif error_stage == ERROR_CURL:
download_comment = (f"`{download_pr_error}`"
f"{download_pr_comments_cfg[CURL_FAILURE]}"
f"{download_pr_comments_cfg[CURL_TIP]}")
elif error_stage == ERROR_GIT_APPLY:
download_comment = (f"`{download_pr_error}`"
f"{download_pr_comments_cfg[GIT_APPLY_FAILURE]}"
f"{download_pr_comments_cfg[GIT_APPLY_TIP]}")

download_comment = pr_comments.create_comment(
repo_name=base_repo_name, pr_number=pr.number, comment=download_comment
)
if download_comment:
log(f"{fn}(): created PR issue comment with id {download_comment.id}")
else:
log(f"{fn}(): failed to create PR issue comment")
raise ValueError("Unable to download PR and/or sync changes")


def apply_cvmfs_customizations(cvmfs_customizations, arch_job_dir):
Expand Down Expand Up @@ -454,8 +540,10 @@ def prepare_jobs(pr, cfg, event_info, action_filter):
log(f"{fn}(): job_dir '{job_dir}'")

# TODO optimisation? download once, copy and cleanup initial copy?
download_pr(base_repo_name, base_branch_name, pr, job_dir)

download_pr_output, download_pr_error, download_pr_exit_code, error_stage = download_pr(
base_repo_name, base_branch_name, pr, job_dir
)
comment_download_pr(base_repo_name, pr, download_pr_exit_code, download_pr_error, error_stage)
# prepare job configuration file 'job.cfg' in directory <job_dir>/cfg
cpu_target = '/'.join(arch.split('/')[1:])
os_type = arch.split('/')[0]
Expand Down
Loading