Skip to content

Commit fa91fcd

Browse files
authored
Merge pull request #259 from EESSI/develop
release v0.4.0
2 parents 6ba9625 + 618a605 commit fa91fcd

13 files changed

+580
-131
lines changed

README.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,20 @@ submit_command = /usr/bin/sbatch
404404
```
405405
`submit_command` is the full path to the Slurm job submission command used for submitting batch jobs. You may want to verify if `sbatch` is provided at that path or determine its actual location (using `which sbatch`).
406406

407+
```
408+
build_permission = GH_ACCOUNT_1 GH_ACCOUNT_2 ...
409+
```
410+
`build_permission` defines which GitHub accounts have the permission to trigger
411+
build jobs, i.e., for which accounts the bot acts on `bot: build ...` commands.
412+
If the value is left empty, everyone can trigger build jobs.
413+
414+
```
415+
no_build_permission_comment = The `bot: build ...` command has been used by user `{build_labeler}`, but this person does not have permission to trigger builds.
416+
```
417+
`no_build_permission_comment` defines a comment (template) that is used when
418+
the account trying to trigger build jobs has no permission to do so.
419+
420+
407421
#### `[bot_control]` section
408422

409423
The `[bot_control]` section contains settings for configuring the feature to
@@ -485,6 +499,43 @@ This defines a message that is added to the status table in a PR comment
485499
corresponding to a job whose tarball should have been uploaded (e.g., after
486500
setting the `bot:deploy` label).
487501

502+
503+
```
504+
metadata_prefix = LOCATION_WHERE_METADATA_FILE_GETS_DEPOSITED
505+
tarball_prefix = LOCATION_WHERE_TARBALL_GETS_DEPOSITED
506+
```
507+
508+
These two settings are used to define where (which directory) in the S3 bucket
509+
(see `bucket_name` above) the metadata file and the tarball will be stored. The
510+
value `LOCATION...` can be a string value to always use the same 'prefix'
511+
regardless of the target CVMFS repository, or can be a mapping of a target
512+
repository id (see also `repo_target_map` below) to a prefix.
513+
514+
The prefix itself can use some (environment) variables that are set within
515+
the upload script (see `tarball_upload_script` above). Currently those are:
516+
* `'${github_repository}'` (which would be expanded to the full name of the GitHub
517+
repository, e.g., `EESSI/software-layer`),
518+
* `'${legacy_aws_path}'` (which expands to the legacy/old prefix being used for
519+
storing tarballs/metadata files, the old prefix is
520+
`EESSI_VERSION/TARBALL_TYPE/OS_TYPE/CPU_ARCHITECTURE/TIMESTAMP/`), _and_
521+
* `'${pull_request_number}'` (which would be expanded to the number of the pull
522+
request from which the tarball originates).
523+
Note, it's important to single-quote (`'`) the variables as shown above, because
524+
they may likely not be defined when the bot calls the upload script.
525+
526+
The list of supported variables can be shown by running
527+
`scripts/eessi-upload-to-staging --list-variables`.
528+
529+
**Examples:**
530+
```
531+
metadata_prefix = {"eessi.io-2023.06": "new/${github_repository}/${pull_request_number}"}
532+
tarball_prefix = {
533+
"eessi-pilot-2023.06": "",
534+
"eessi.io-2023.06": "new/${github_repository}/${pull_request_number}"
535+
}
536+
```
537+
If left empty, the old/legacy prefix is being used.
538+
488539
#### `[architecturetargets]` section
489540

490541
The section `[architecturetargets]` defines for which targets (OS/SUBDIR), (for example `linux/x86_64/amd/zen2`) the EESSI bot should submit jobs, and which additional `sbatch` parameters will be used for requesting a compute node with the CPU microarchitecture needed to build the software stack.
@@ -657,6 +708,53 @@ job_test_unknown_fmt = <details><summary>:shrug: UNKNOWN _(click triangle for de
657708
`job_test_unknown_fmt` is used in case no test file (produced by `bot/check-test.sh`
658709
provided by target repository) was found.
659710

711+
712+
#### `[download_pr_comments]` section
713+
714+
The `[download_pr_comments]` section sets templates for messages related to
715+
downloading the contents of a pull request.
716+
```
717+
git_clone_failure = Unable to clone the target repository.
718+
```
719+
`git_clone_failure` is shown when `git clone` failed.
720+
721+
```
722+
git_clone_tip = _Tip: This could be a connection failure. Try again and if the issue remains check if the address is correct_.
723+
```
724+
`git_clone_tip` should contain some hint on how to deal with the issue. It is shown when `git clone` failed.
725+
726+
```
727+
git_checkout_failure = Unable to checkout to the correct branch.
728+
```
729+
`git_checkout_failure` is shown when `git checkout` failed.
730+
731+
```
732+
git_checkout_tip = _Tip: Ensure that the branch name is correct and the target branch is available._
733+
```
734+
`git_checkout_tip` should contain some hint on how to deal with the failure. It
735+
is shown when `git checkout` failed.
736+
737+
```
738+
curl_failure = Unable to download the `.diff` file.
739+
```
740+
`curl_failure` is shown when downloading the `PR_NUMBER.diff`
741+
```
742+
curl_tip = _Tip: This could be a connection failure. Try again and if the issue remains check if the address is correct_
743+
```
744+
`curl_tip` should help in how to deal with failing downloads of the `.diff` file.
745+
746+
```
747+
git_apply_failure = Unable to download or merge changes between the source branch and the destination branch.
748+
```
749+
`git_apply_failure` is shown when applying the `.diff` file with `git apply`
750+
failed.
751+
752+
```
753+
git_apply_tip = _Tip: This can usually be resolved by syncing your branch and resolving any merge conflicts._
754+
```
755+
`git_apply_tip` should guide the contributor/maintainer about resolving the cause
756+
of `git apply` failing.
757+
660758
# Instructions to run the bot components
661759

662760
The bot consists of three components:

RELEASE_NOTES

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
This file contains a description of the major changes to the EESSI
22
build-and-deploy bot. For more detailed information, please see the git log.
33

4+
v0.4.0 (28 February 2024)
5+
--------------------------
6+
7+
This is a minor release of the EESSI build-and-deploy bot.
8+
9+
Bug fixes:
10+
* fixes issue using wrong values when using the `bot: status` command (#251)
11+
12+
Improvements:
13+
* make bot report when preparing the job working directory failed, for example due to merge conflict in a pull request (#248)
14+
* adding the pull request comment id to the metadata file that is uploaded to the
15+
the S3 bucket (#247, #249, #250, #253)
16+
* enabling configurable upload directories for tarball and metadata file (#254)
17+
* only make bot respond to pull request comments that contain a bot command (#257)
18+
19+
420
v0.3.0 (30 January 2024)
521
--------------------------
622

app.cfg.example

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,28 @@ deploy_permission =
147147
# template for comment when user who set a label has no permission to trigger deploying tarballs
148148
no_deploy_permission_comment = Label `bot:deploy` has been set by user `{deploy_labeler}`, but this person does not have permission to trigger deployments
149149

150+
# settings for where (directory) in the S3 bucket to store the metadata file and
151+
# the tarball
152+
# - Can be a string value to always use the same 'prefix' regardless of the target
153+
# CVMFS repository, or can be a mapping of a target repository id (see also
154+
# repo_target_map) to a prefix.
155+
# - The prefix itself can use some (environment) variables that are set within
156+
# the script. Currently those are:
157+
# * 'github_repository' (which would be expanded to the full name of the GitHub
158+
# repository, e.g., 'EESSI/software-layer'),
159+
# * 'legacy_aws_path' (which expands to the legacy/old prefix being used for
160+
# storing tarballs/metadata files) and
161+
# * 'pull_request_number' (which would be expanded to the number of the pull
162+
# request from which the tarball originates).
163+
# - The list of supported variables can be shown by running
164+
# `scripts/eessi-upload-to-staging --list-variables`.
165+
# - Examples:
166+
# metadata_prefix = {"eessi.io-2023.06": "new/${github_repository}/${pull_request_number}"}
167+
# tarball_prefix = {"eessi-pilot-2023.06": "", "eessi.io-2023.06": "new/${github_repository}/${pull_request_number}"}
168+
# If left empty, the old/legacy prefix is being used.
169+
metadata_prefix =
170+
tarball_prefix =
171+
150172

151173
[architecturetargets]
152174
# defines both for which architectures the bot will build
@@ -219,3 +241,13 @@ no_matching_tarball = No tarball matching `{tarball_pattern}` found in job dir.
219241
multiple_tarballs = Found {num_tarballs} tarballs in job dir - only 1 matching `{tarball_pattern}` expected.
220242
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>
221243
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>
244+
245+
[download_pr_comments]
246+
git_clone_failure = Unable to clone the target repository.
247+
git_clone_tip = _Tip: This could be a connection failure. Try again and if the issue remains check if the address is correct_.
248+
git_checkout_failure = Unable to checkout to the correct branch.
249+
git_checkout_tip = _Tip: Ensure that the branch name is correct and the target branch is available._
250+
curl_failure = Unable to download the `.diff` file.
251+
curl_tip = _Tip: This could be a connection failure. Try again and if the issue remains check if the address is correct_
252+
git_apply_failure = Unable to download or merge changes between the source branch and the destination branch.
253+
git_apply_tip = _Tip: This can usually be resolved by syncing your branch and resolving any merge conflicts._

eessi_bot_event_handler.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
from tasks.deploy import deploy_built_artefacts
3232
from tools import config
3333
from tools.args import event_handler_parse
34-
from tools.commands import EESSIBotCommand, EESSIBotCommandError, get_bot_command
34+
from tools.commands import EESSIBotCommand, EESSIBotCommandError, \
35+
contains_any_bot_command, get_bot_command
3536
from tools.permissions import check_command_permission
3637
from tools.pr_comments import create_comment
3738

@@ -113,7 +114,25 @@ def handle_issue_comment_event(self, event_info, log_file=None):
113114
# currently, only commands in new comments are supported
114115
# - commands have the syntax 'bot: COMMAND [ARGS*]'
115116

116-
# first check if sender is authorized to send any command
117+
# only scan for commands in newly created comments
118+
if action == 'created':
119+
comment_received = request_body['comment']['body']
120+
self.log(f"comment action '{action}' is handled")
121+
else:
122+
# NOTE we do not respond to an updated PR comment with yet another
123+
# new PR comment, because it would make the bot very noisy or
124+
# worse could result in letting the bot enter an endless loop
125+
self.log(f"comment action '{action}' not handled")
126+
return
127+
# at this point we know that we are handling a new comment
128+
129+
# check if comment does not contain a bot command
130+
if not contains_any_bot_command(comment_received):
131+
self.log("comment does not contain a bot comment; not processing it further")
132+
return
133+
# at this point we know that the comment contains a bot command
134+
135+
# check if sender is authorized to send any command
117136
# - this serves a double purpose:
118137
# 1. check permission
119138
# 2. skip any comment updates that were done by the bot itself
@@ -150,17 +169,6 @@ def handle_issue_comment_event(self, event_info, log_file=None):
150169
else:
151170
self.log(f"account `{sender}` has permission to send commands to bot")
152171

153-
# only scan for commands in newly created comments
154-
if action == 'created':
155-
comment_received = request_body['comment']['body']
156-
self.log(f"comment action '{action}' is handled")
157-
else:
158-
# NOTE we do not respond to an updated PR comment with yet another
159-
# new PR comment, because it would make the bot very noisy or
160-
# worse could result in letting the bot enter an endless loop
161-
self.log(f"comment action '{action}' not handled")
162-
return
163-
164172
# search for commands in comment
165173
comment_response = ''
166174
commands = []

eessi_bot_job_manager.py

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
from connections import github
4343
from tools import config, run_cmd
4444
from tools.args import job_manager_parse
45-
from tools.job_metadata import read_metadata_file
45+
from tools.job_metadata import read_job_metadata_from_file, read_metadata_file
4646
from tools.pr_comments import get_submitted_job_comment, update_comment
4747

4848

@@ -251,24 +251,6 @@ def determine_finished_jobs(self, known_jobs, current_jobs):
251251

252252
return finished_jobs
253253

254-
def read_job_pr_metadata(self, job_metadata_path):
255-
"""
256-
Read job metadata file and return the contents of the 'PR' section.
257-
258-
Args:
259-
job_metadata_path (string): path to job metadata file
260-
261-
Returns:
262-
(ConfigParser): instance of ConfigParser corresponding to the 'PR'
263-
section or None
264-
"""
265-
# reuse function from module tools.job_metadata to read metadata file
266-
metadata = read_metadata_file(job_metadata_path, self.logfile)
267-
if metadata and "PR" in metadata:
268-
return metadata["PR"]
269-
else:
270-
return None
271-
272254
def read_job_result(self, job_result_file_path):
273255
"""
274256
Read job result file and return the contents of the 'RESULT' section.
@@ -350,7 +332,7 @@ def process_new_job(self, new_job):
350332

351333
# assuming that a bot job's working directory contains a metadata
352334
# file, its existence is used to check if the job belongs to the bot
353-
metadata_pr = self.read_job_pr_metadata(job_metadata_path)
335+
metadata_pr = read_job_metadata_from_file(job_metadata_path, self.logfile)
354336

355337
if metadata_pr is None:
356338
log(f"No metadata file found at {job_metadata_path} for job {job_id}, so skipping it",
@@ -446,7 +428,7 @@ def process_running_jobs(self, running_job):
446428
job_metadata_path = os.path.join(job_dir, metadata_file)
447429

448430
# check if metadata file exist
449-
metadata_pr = self.read_job_pr_metadata(job_metadata_path)
431+
metadata_pr = read_job_metadata_from_file(job_metadata_path, self.logfile)
450432
if metadata_pr is None:
451433
raise Exception("Unable to find metadata file")
452434

@@ -591,7 +573,7 @@ def process_finished_job(self, finished_job):
591573
# obtain id of PR comment to be updated (from file '_bot_jobID.metadata')
592574
metadata_file = f"_bot_job{job_id}.metadata"
593575
job_metadata_path = os.path.join(new_symlink, metadata_file)
594-
metadata_pr = self.read_job_pr_metadata(job_metadata_path)
576+
metadata_pr = read_job_metadata_from_file(job_metadata_path, self.logfile)
595577
if metadata_pr is None:
596578
raise Exception("Unable to find metadata file ... skip updating PR comment")
597579

@@ -685,14 +667,26 @@ def main():
685667
if max_iter != 0:
686668
known_jobs = job_manager.get_known_jobs()
687669
while max_iter < 0 or i < max_iter:
670+
# sleep poll_interval seconds (not for the first iteration)
671+
if i != 0:
672+
log(
673+
"job manager main loop: sleep %d seconds" % poll_interval,
674+
job_manager.logfile,
675+
)
676+
time.sleep(poll_interval)
688677
log("job manager main loop: iteration %d" % i, job_manager.logfile)
689678
log(
690679
"job manager main loop: known_jobs='%s'" % ",".join(
691680
known_jobs.keys()),
692681
job_manager.logfile,
693682
)
694683

695-
current_jobs = job_manager.get_current_jobs()
684+
try:
685+
current_jobs = job_manager.get_current_jobs()
686+
except RuntimeError:
687+
i = i + 1
688+
continue
689+
696690
log(
697691
"job manager main loop: current_jobs='%s'" % ",".join(
698692
current_jobs.keys()),
@@ -747,13 +741,7 @@ def main():
747741

748742
known_jobs = current_jobs
749743

750-
# sleep poll_interval seconds (only if at least one more iteration)
751-
if max_iter < 0 or i + 1 < max_iter:
752-
log(
753-
"job manager main loop: sleep %d seconds" % poll_interval,
754-
job_manager.logfile,
755-
)
756-
time.sleep(poll_interval)
744+
# add one iteration to the loop
757745
i = i + 1
758746

759747

0 commit comments

Comments
 (0)