Skip to content

Commit 50c21d7

Browse files
authored
Merge pull request #300 from boegel/git_clone_via_ssh
add support for cloning Git repository via SSH rather than HTTPS
2 parents 4b7c50c + 750b8a4 commit 50c21d7

File tree

5 files changed

+97
-18
lines changed

5 files changed

+97
-18
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,29 @@ allowed_exportvars = ["SKIP_TESTS=yes", "SKIP_TESTS=no"]
485485
```
486486

487487

488+
```
489+
clone_git_repo_via = https
490+
```
491+
492+
The `clone_git_repo_via` setting specifies via which mechanism the Git repository
493+
should be cloned. This can be either:
494+
* `https` (default): clone repository via HTTPS with `git clone https://github.com/<owner>/<repo>`
495+
* `ssh`: clone repository via SSH with `git clone git@github.com:<owner>/<repo>.git`
496+
In case of using 'ssh', one may need additional steps to ensure that the bot uses the right SSH key and does not ask for a passphrase (if the key used is protected with one). Here are a few things to consider:
497+
- if the ssh key to be used does not have a standard name (e.g., `id_rsa`), add the following entry to `~/.ssh/config` in the bot's account
498+
```
499+
Host github.com
500+
User git
501+
IdentityFile ~/.ssh/NAME_OF_PRIVATE_KEY_FILE
502+
```
503+
- if the key is protected by a passphrase (**highly recommended**), run an SSH agent and add the key to it
504+
```
505+
eval $(ssh-agent -s)
506+
ssh-add ~/.ssh/NAME_OF_PRIVATE_KEY_FILE
507+
```
508+
509+
Note that the `bot: status` command doesn't work with SSH keys; you'll still need a Github token for that to work.
510+
488511
#### `[bot_control]` section
489512

490513
The `[bot_control]` section contains settings for configuring the feature to

app.cfg.example

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,25 @@ allow_update_submit_opts = false
175175
# "SKIP_TESTS=yes".
176176
# allowed_exportvars = ["NAME1=value_1a", "NAME1=value_1b", "NAME2=value_2"]
177177

178+
# mechanisn to use to clone Git repository
179+
# 'https' to clone via HTTPS (git clone https://github.com/<owner>/<repo>)
180+
# In case of using 'ssh', one may need additional steps to ensure that the bot
181+
# uses the right ssh key and does not ask for a passphrase (if the key used is
182+
# protected with one). Here are a few things to consider:
183+
# - if the ssh key to be used does not have a standard name (e.g., 'id_rsa'),
184+
# add the following entry to '~/.ssh/config' in the bot's account
185+
#
186+
# Host github.com
187+
# User git
188+
# IdentityFile ~/.ssh/NAME_OF_PRIVATE_KEY_FILE
189+
#
190+
# - if the key is protected by a passphrase (**highly recommended**), run an
191+
# SSH agent and add the key to it (with the following two commands)
192+
#
193+
# eval $(ssh-agent -s)
194+
# ssh-add ~/.ssh/NAME_OF_PRIVATE_KEY_FILE
195+
196+
clone_git_repo_via = https
178197

179198
[deploycfg]
180199
# script for uploading built software packages
@@ -338,6 +357,8 @@ curl_failure = Unable to download the `.diff` file.
338357
curl_tip = _Tip: This could be a connection failure. Try again and if the issue remains check if the address is correct_
339358
git_apply_failure = Unable to download or merge changes between the source branch and the destination branch.
340359
git_apply_tip = _Tip: This can usually be resolved by syncing your branch and resolving any merge conflicts._
360+
pr_diff_failure = Unable to obtain PR diff.
361+
pr_diff_tip = _Tip: This could be a problem with SSH access to the repository._
341362

342363
[clean_up]
343364
trash_bin_dir = $HOME/trash_bin

eessi_bot_event_handler.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
config.BUILDENV_SETTING_BUILD_LOGS_DIR, # optional+recommended
5555
config.BUILDENV_SETTING_BUILD_PERMISSION, # optional+recommended
5656
config.BUILDENV_SETTING_CONTAINER_CACHEDIR, # optional+recommended
57+
# config.BUILDENV_SETTING_CLONE_GIT_REPO_VIA, # optional
5758
# config.BUILDENV_SETTING_CVMFS_CUSTOMIZATIONS, # optional
5859
# config.BUILDENV_SETTING_HTTPS_PROXY, # optional
5960
# config.BUILDENV_SETTING_HTTP_PROXY, # optional
@@ -88,7 +89,9 @@
8889
config.DOWNLOAD_PR_COMMENTS_SETTING_GIT_CHECKOUT_FAILURE, # required
8990
config.DOWNLOAD_PR_COMMENTS_SETTING_GIT_CHECKOUT_TIP, # required
9091
config.DOWNLOAD_PR_COMMENTS_SETTING_GIT_CLONE_FAILURE, # required
91-
config.DOWNLOAD_PR_COMMENTS_SETTING_GIT_CLONE_TIP], # required
92+
config.DOWNLOAD_PR_COMMENTS_SETTING_GIT_CLONE_TIP, # required
93+
config.DOWNLOAD_PR_COMMENTS_SETTING_PR_DIFF_FAILURE, # required
94+
config.DOWNLOAD_PR_COMMENTS_SETTING_PR_DIFF_TIP], # required
9295
config.SECTION_EVENT_HANDLER: [
9396
config.EVENT_HANDLER_SETTING_LOG_PATH], # required
9497
config.SECTION_GITHUB: [

tasks/build.py

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
_ERROR_CURL = "curl"
4343
_ERROR_GIT_APPLY = "git apply"
4444
_ERROR_GIT_CHECKOUT = "git checkout"
45-
_ERROR_GIT_CLONE = "curl"
45+
_ERROR_GIT_CLONE = "git clone"
46+
_ERROR_PR_DIFF = "pr_diff"
4647
_ERROR_NONE = "none"
4748

4849
# other constants
@@ -171,6 +172,10 @@ def get_build_env_cfg(cfg):
171172
log(f"{fn}(): load_modules '{load_modules}'")
172173
config_data[config.BUILDENV_SETTING_LOAD_MODULES] = load_modules
173174

175+
clone_git_repo_via = buildenv.get(config.BUILDENV_SETTING_CLONE_GIT_REPO_VIA, None)
176+
log(f"{fn}(): clone_git_repo_via '{clone_git_repo_via}'")
177+
config_data[config.BUILDENV_SETTING_CLONE_GIT_REPO_VIA] = clone_git_repo_via
178+
174179
return config_data
175180

176181

@@ -379,7 +384,7 @@ def clone_git_repo(repo, path):
379384
return (clone_output, clone_error, clone_exit_code)
380385

381386

382-
def download_pr(repo_name, branch_name, pr, arch_job_dir):
387+
def download_pr(repo_name, branch_name, pr, arch_job_dir, clone_via=None):
383388
"""
384389
Download pull request to job working directory
385390
@@ -388,6 +393,7 @@ def download_pr(repo_name, branch_name, pr, arch_job_dir):
388393
branch_name (string): name of the base branch of the pull request
389394
pr (github.PullRequest.PullRequest): instance representing the pull request
390395
arch_job_dir (string): working directory of the job to be submitted
396+
clone_via (string): mechanism to clone Git repository, should be 'https' (default) or 'ssh'
391397
392398
Returns:
393399
None (implicitly), in case an error is caught in the git clone, git checkout, curl,
@@ -400,7 +406,29 @@ def download_pr(repo_name, branch_name, pr, arch_job_dir):
400406
# - 'git checkout' base branch of pull request
401407
# - 'curl' diff for pull request
402408
# - 'git apply' diff file
403-
clone_output, clone_error, clone_exit_code = clone_git_repo(f'https://github.com/{repo_name}', arch_job_dir)
409+
log(f"Cloning Git repo via: {clone_via}")
410+
if clone_via in (None, 'https'):
411+
repo_url = f'https://github.com/{repo_name}'
412+
pr_diff_cmd = ' '.join([
413+
'curl -L',
414+
'-H "Accept: application/vnd.github.diff"',
415+
'-H "X-GitHub-Api-Version: 2022-11-28"',
416+
f'https://api.github.com/repos/{repo_name}/pulls/{pr.number} > {pr.number}.diff',
417+
])
418+
elif clone_via == 'ssh':
419+
repo_url = f'git@github.com:{repo_name}.git'
420+
pr_diff_cmd = ' && '.join([
421+
f"git fetch origin pull/{pr.number}/head:pr{pr.number}",
422+
f"git diff $(git merge-base pr{pr.number} HEAD) pr{pr.number} > {pr.number}.diff",
423+
])
424+
else:
425+
clone_output = ''
426+
clone_error = f"Unknown mechanism to clone Git repo: {clone_via}"
427+
clone_exit_code = 1
428+
error_stage = _ERROR_GIT_CLONE
429+
return clone_output, clone_error, clone_exit_code, error_stage
430+
431+
clone_output, clone_error, clone_exit_code = clone_git_repo(repo_url, arch_job_dir)
404432
if clone_exit_code != 0:
405433
error_stage = _ERROR_GIT_CLONE
406434
return clone_output, clone_error, clone_exit_code, error_stage
@@ -417,24 +445,18 @@ def download_pr(repo_name, branch_name, pr, arch_job_dir):
417445
error_stage = _ERROR_GIT_CHECKOUT
418446
return checkout_output, checkout_err, checkout_exit_code, error_stage
419447

420-
curl_cmd = ' '.join([
421-
'curl -L',
422-
'-H "Accept: application/vnd.github.diff"',
423-
'-H "X-GitHub-Api-Version: 2022-11-28"',
424-
f'https://api.github.com/repos/{repo_name}/pulls/{pr.number} > {pr.number}.diff',
425-
])
426-
log(f'curl with command {curl_cmd}')
427-
curl_output, curl_error, curl_exit_code = run_cmd(
428-
curl_cmd, "Obtain patch", arch_job_dir, raise_on_error=False
448+
log(f'obtaining PR diff with command {pr_diff_cmd}')
449+
pr_diff_output, pr_diff_error, pr_diff_exit_code = run_cmd(
450+
pr_diff_cmd, "obtain PR diff", arch_job_dir, raise_on_error=False
429451
)
430-
if curl_exit_code != 0:
431-
error_stage = _ERROR_CURL
432-
return curl_output, curl_error, curl_exit_code, error_stage
452+
if pr_diff_exit_code != 0:
453+
error_stage = _ERROR_PR_DIFF
454+
return pr_diff_output, pr_diff_error, pr_diff_exit_code, error_stage
433455

434456
git_apply_cmd = f'git apply {pr.number}.diff'
435457
log(f'git apply with command {git_apply_cmd}')
436458
git_apply_output, git_apply_error, git_apply_exit_code = run_cmd(
437-
git_apply_cmd, "Apply patch", arch_job_dir, raise_on_error=False
459+
git_apply_cmd, "apply patch", arch_job_dir, raise_on_error=False
438460
)
439461
if git_apply_exit_code != 0:
440462
error_stage = _ERROR_GIT_APPLY
@@ -481,6 +503,12 @@ def comment_download_pr(base_repo_name, pr, download_pr_exit_code, download_pr_e
481503
download_comment = (f"```{download_pr_error}```\n"
482504
f"{download_pr_comments_cfg[config.DOWNLOAD_PR_COMMENTS_SETTING_GIT_APPLY_FAILURE]}"
483505
f"\n{download_pr_comments_cfg[config.DOWNLOAD_PR_COMMENTS_SETTING_GIT_APPLY_TIP]}")
506+
elif error_stage == _ERROR_PR_DIFF:
507+
download_comment = (f"```{download_pr_error}```\n"
508+
f"{download_pr_comments_cfg[config.DOWNLOAD_PR_COMMENTS_SETTING_PR_DIFF_FAILURE]}"
509+
f"\n{download_pr_comments_cfg[config.DOWNLOAD_PR_COMMENTS_SETTING_PR_DIFF_TIP]}")
510+
else:
511+
download_comment = f"```{download_pr_error}```"
484512

485513
download_comment = pr_comments.create_comment(
486514
repo_name=base_repo_name, pr_number=pr.number, comment=download_comment, req_chatlevel=ChatLevels.MINIMAL
@@ -639,8 +667,9 @@ def prepare_jobs(pr, cfg, event_info, action_filter):
639667
log(f"{fn}(): job_dir '{job_dir}'")
640668

641669
# TODO optimisation? download once, copy and cleanup initial copy?
670+
clone_git_repo_via = build_env_cfg.get(config.BUILDENV_SETTING_CLONE_GIT_REPO_VIA)
642671
download_pr_output, download_pr_error, download_pr_exit_code, error_stage = download_pr(
643-
base_repo_name, base_branch_name, pr, job_dir
672+
base_repo_name, base_branch_name, pr, job_dir, clone_via=clone_git_repo_via,
644673
)
645674
comment_download_pr(base_repo_name, pr, download_pr_exit_code, download_pr_error, error_stage)
646675
# prepare job configuration file 'job.cfg' in directory <job_dir>/cfg

tools/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
BUILDENV_SETTING_BUILD_JOB_SCRIPT = 'build_job_script'
4444
BUILDENV_SETTING_BUILD_LOGS_DIR = 'build_logs_dir'
4545
BUILDENV_SETTING_BUILD_PERMISSION = 'build_permission'
46+
BUILDENV_SETTING_CLONE_GIT_REPO_VIA = 'clone_git_repo_via'
4647
BUILDENV_SETTING_CONTAINER_CACHEDIR = 'container_cachedir'
4748
BUILDENV_SETTING_CVMFS_CUSTOMIZATIONS = 'cvmfs_customizations'
4849
BUILDENV_SETTING_HTTPS_PROXY = 'https_proxy'
@@ -82,6 +83,8 @@
8283
DOWNLOAD_PR_COMMENTS_SETTING_GIT_CHECKOUT_TIP = 'git_checkout_tip'
8384
DOWNLOAD_PR_COMMENTS_SETTING_GIT_CLONE_FAILURE = 'git_clone_failure'
8485
DOWNLOAD_PR_COMMENTS_SETTING_GIT_CLONE_TIP = 'git_clone_tip'
86+
DOWNLOAD_PR_COMMENTS_SETTING_PR_DIFF_FAILURE = 'pr_diff_failure'
87+
DOWNLOAD_PR_COMMENTS_SETTING_PR_DIFF_TIP = 'pr_diff_tip'
8588

8689
SECTION_EVENT_HANDLER = 'event_handler'
8790
EVENT_HANDLER_SETTING_LOG_PATH = 'log_path'

0 commit comments

Comments
 (0)