Skip to content

Commit 95c574b

Browse files
authored
Merge pull request #319 from EESSI/develop
merging `develop` into `main` for release v0.8.0
2 parents 4d9dc0e + 450973d commit 95c574b

15 files changed

Lines changed: 326 additions & 135 deletions

.github/workflows/tests.yaml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ on: [push, pull_request]
1717
permissions: read-all
1818
jobs:
1919
test:
20-
runs-on: ubuntu-20.04
20+
runs-on: ubuntu-24.04
2121
strategy:
2222
matrix:
23-
python: [3.6, 3.7, 3.8, 3.9, '3.10', '3.11']
23+
# for now, only test with Python 3.9+ (since we're testing in Ubuntu 24.04)
24+
#python: [3.6, 3.7, 3.8, 3.9, '3.10', '3.11']
25+
python: ['3.9', '3.10', '3.11']
2426
fail-fast: false
2527
steps:
2628
- name: checkout
@@ -38,9 +40,14 @@ jobs:
3840
python -m pip install pytest
3941
python -m pip install --upgrade flake8
4042
41-
- name: Run test suite
43+
- name: Run test suite (without coverage)
4244
run: |
43-
./test.sh
45+
./test.sh --verbose
46+
47+
- name: Run test suite (with coverage)
48+
run: |
49+
python -m pip install pytest-cov
50+
./test.sh -q --cov=$PWD
4451
4552
- name: Run flake8 to verify PEP8-compliance of Python code
4653
run: |

.github/workflows/tests_scripts.yml

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,71 @@ on:
44
push:
55
paths:
66
- scripts/sign_verify_file_ssh.sh
7+
- .github/workflows/tests_scripts.yml
78
pull_request:
89
paths:
910
- scripts/sign_verify_file_ssh.sh
11+
- .github/workflows/tests_scripts.yml
1012
permissions:
1113
contents: read # to fetch code (actions/checkout)
1214
jobs:
1315
build:
1416
runs-on: ubuntu-24.04
1517
steps:
16-
- name: checkout
17-
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
18+
- name: Checkout repository
19+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
1820

19-
- name: test sign_verify_file_ssh.sh script
20-
run: |
21-
# Create a PEM format ssh identity
21+
- name: Prepare SSH key pair, file and signature
22+
run: |
2223
ssh-keygen -t rsa -b 4096 -m PEM -f id_rsa.pem -N ""
23-
# Create a file to sign
2424
echo "Very important stuff" > out.txt
2525
export FILE_TO_SIGN="out.txt"
26-
# Sign the file
27-
./scripts/sign_verify_file_ssh.sh sign id_rsa.pem "$FILE_TO_SIGN"
28-
# Create an allowed_signers file based on the public key
29-
echo -n "allowed_identity " > allowed_signers
26+
./scripts/sign_verify_file_ssh.sh --sign --private-key id_rsa.pem --file "$FILE_TO_SIGN" --namespace ci
27+
28+
- name: Create allowed signers file and verify
29+
run: |
30+
valid_before=$(date --date='today+3days' +%Y%m%d)
31+
echo -n 'allowed_identity namespaces="ci",valid-before="'$valid_before'" ' > allowed_signers
3032
cat id_rsa.pem.pub >> allowed_signers
31-
# Verify the signature
32-
./scripts/sign_verify_file_ssh.sh verify allowed_signers "$FILE_TO_SIGN"
33-
# Make a new signature that does not appear in the allowed signers file
33+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt
34+
35+
- name: Replace allowed signers with disallowed identity
36+
run: |
37+
valid_before=$(date --date='today+3days' +%Y%m%d)
3438
ssh-keygen -t rsa -b 4096 -m PEM -f id_rsa.alt.pem -N ""
35-
# Replace the allowed signers file
36-
echo -n "disallowed_identity " > allowed_signers
39+
echo -n 'disallowed_identity namespaces="ci",valid-before="'$valid_before'" ' > allowed_signers
3740
cat id_rsa.alt.pem.pub >> allowed_signers
38-
# Make sure signature checking fails in this case
39-
./scripts/sign_verify_file_ssh.sh verify allowed_signers "$FILE_TO_SIGN" && exit 1 || echo "Expected failure for unknown identity"
41+
42+
- name: Ensure verification fails for unknown identity
43+
run: |
44+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt && exit 1 || echo "Expected failure for unknown identity"
45+
46+
- name: Replace allowed signers with wrong namespace
47+
run: |
48+
valid_before=$(date --date='today+3days' +%Y%m%d)
49+
echo -n 'wrong_namespace_identity namespaces="CI",valid-before="'$valid_before'" ' > allowed_signers
50+
cat id_rsa.pem.pub >> allowed_signers
51+
52+
- name: Ensure verification fails for wrong namespace
53+
run: |
54+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt && exit 2 || echo "Expected failure for wrong namespace"
55+
56+
- name: Replace allowed signers with expired key
57+
run: |
58+
valid_expired=$(date --date='today-3days' +%Y%m%d)
59+
echo -n 'expired_key_identity namespaces="ci",valid-before="'$valid_expired'" ' > allowed_signers
60+
cat id_rsa.pem.pub >> allowed_signers
61+
62+
- name: Ensure verification fails for expired key
63+
run: |
64+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt && exit 3 || echo "Expected failure for expired key"
65+
66+
- name: Ensure verification when looping through allowed signers file
67+
run: |
68+
# Add the approved identity to the end
69+
valid_before=$(date --date='today+3days' +%Y%m%d)
70+
echo -n 'listed_identity namespaces="ci",valid-before="'$valid_before'" ' >> allowed_signers
71+
cat id_rsa.pem.pub >> allowed_signers
72+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt
73+
# Make sure we get exactly what we want in terse mode
74+
./scripts/sign_verify_file_ssh.sh --verify --allowed-signers-file allowed_signers --file out.txt --terse | grep -q '{\"identity\": \"listed_identity\", \"namespace\": \"ci\"}'

RELEASE_NOTES

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
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.8.0 (23 May 2025)
5+
--------------------------
6+
7+
This is a minor release of the EESSI build-and-deploy bot.
8+
9+
Bug fixes:
10+
* use Ubuntu 24.04 in CI (#316)
11+
* delete pre-existing signature files (#309)
12+
13+
Improvements:
14+
* adding argument `--contain` when launching the build container (#307)
15+
* use the bot instance's name as `namespaces` value in a signature (#308)
16+
* determine test coverage (#311)
17+
* support different levels for when a bot instance creates comments on GitHub (#315)
18+
19+
Changes to 'app.cfg' settings (see README.md and app.cfg.example for details):
20+
* NEW (optional) 'chatlevel' in section '[bot_control]'
21+
22+
423
v0.7.0 (13 March 2025)
524
--------------------------
625

app.cfg.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ command_response_fmt =
6161
{comment_result}
6262
</details>
6363

64+
# chattiness level of the bot in terms of writing comments into PRs (minimal, basic, or chatty)
65+
chatlevel = basic
6466

6567
[buildenv]
6668
# name of the job script used for building an EESSI stack

connections/github.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def get_instance():
101101
Returns:
102102
Instance of Github
103103
"""
104-
global _gh, _token
104+
global _gh
105105

106106
# Check if PyGithub version is < 1.56
107107
if hasattr(github, 'GithubRetry'):
@@ -129,5 +129,4 @@ def token():
129129
Returns:
130130
Token
131131
"""
132-
global _token
133132
return _token

eessi_bot_event_handler.py

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,14 @@
3838
from tools.commands import EESSIBotCommand, EESSIBotCommandError, \
3939
contains_any_bot_command, get_bot_command
4040
from tools.permissions import check_command_permission
41-
from tools.pr_comments import create_comment
41+
from tools.pr_comments import ChatLevels, create_comment
4242

4343

4444
REQUIRED_CONFIG = {
4545
config.SECTION_ARCHITECTURETARGETS: [
4646
config.ARCHITECTURETARGETS_SETTING_ARCH_TARGET_MAP], # required
4747
config.SECTION_BOT_CONTROL: [
48+
# config.BOT_CONTROL_SETTING_CHATLEVEL, # optional
4849
config.BOT_CONTROL_SETTING_COMMAND_PERMISSION, # required
4950
config.BOT_CONTROL_SETTING_COMMAND_RESPONSE_FMT], # required
5051
config.SECTION_BUILDENV: [
@@ -193,6 +194,8 @@ def handle_issue_comment_event(self, event_info, log_file=None):
193194
return
194195
# at this point we know that we are handling a new comment
195196

197+
issue_comment = None
198+
196199
# check if comment does not contain a bot command
197200
if not contains_any_bot_command(comment_received):
198201
self.log("comment does not contain a bot comment; not processing it further")
@@ -229,7 +232,7 @@ def handle_issue_comment_event(self, event_info, log_file=None):
229232
comment_response=comment_response,
230233
comment_result=''
231234
)
232-
issue_comment = create_comment(repo_name, pr_number, comment_body)
235+
issue_comment = create_comment(repo_name, pr_number, comment_body, ChatLevels.CHATTY)
233236
else:
234237
self.log(f"account `{sender}` seems to be a bot instance itself, hence not creating a new PR comment")
235238
return
@@ -263,6 +266,11 @@ def handle_issue_comment_event(self, event_info, log_file=None):
263266
# including a bot command; the logging should only be done when log
264267
# level is set to debug
265268

269+
if 'help' in (x.command for x in commands):
270+
req_chatlevel = ChatLevels.MINIMAL
271+
else:
272+
req_chatlevel = ChatLevels.CHATTY
273+
266274
if comment_response == '':
267275
# no update to be added, just log and return
268276
self.log("comment response is empty")
@@ -281,7 +289,7 @@ def handle_issue_comment_event(self, event_info, log_file=None):
281289
comment_response=comment_response,
282290
comment_result=''
283291
)
284-
issue_comment = create_comment(repo_name, pr_number, comment_body)
292+
issue_comment = create_comment(repo_name, pr_number, comment_body, req_chatlevel)
285293
else:
286294
self.log(f"update '{comment_response}' is considered to contain bot command ... not creating PR comment")
287295
# TODO we may want to report this back to the PR on GitHub, e.g.,
@@ -306,24 +314,26 @@ def handle_issue_comment_event(self, event_info, log_file=None):
306314
continue
307315
except Exception as err:
308316
log(f"Unexpected err={err}, type(err)={type(err)}")
309-
if comment_result:
317+
if comment_result and issue_comment:
310318
comment_body = command_response_fmt.format(
311319
app_name=app_name,
312320
comment_response=comment_response,
313321
comment_result=comment_result
314322
)
315323
issue_comment.edit(comment_body)
316324
raise
317-
# only update PR comment once, that is, a single call to
318-
# issue_comment.edit is made in the entire function
319-
comment_body = command_response_fmt.format(
320-
app_name=app_name,
321-
comment_response=comment_response,
322-
comment_result=comment_result
323-
)
324-
issue_comment.edit(comment_body)
325325

326-
self.log(f"issue_comment event (url {issue_url}) handled!")
326+
if issue_comment:
327+
# only update PR comment once, that is, a single call to
328+
# issue_comment.edit is made in the entire function
329+
comment_body = command_response_fmt.format(
330+
app_name=app_name,
331+
comment_response=comment_response,
332+
comment_result=comment_result
333+
)
334+
issue_comment.edit(comment_body)
335+
336+
self.log(f"issue_comment event (url {issue_url}) handled!")
327337

328338
def handle_installation_event(self, event_info, log_file=None):
329339
"""
@@ -373,14 +383,14 @@ def handle_pull_request_labeled_event(self, event_info, pr):
373383
comment_response=msg,
374384
comment_result=''
375385
)
376-
create_comment(repo_name, pr_number, comment_body)
386+
create_comment(repo_name, pr_number, comment_body, ChatLevels.BASIC)
377387
elif label == "bot:deploy":
378388
# run function to deploy built artefacts
379389
deploy_built_artefacts(pr, event_info)
380390
else:
381391
self.log("handle_pull_request_labeled_event: no handler for label '%s'", label)
382392

383-
def handle_pull_request_opened_event(self, event_info, pr):
393+
def handle_pull_request_opened_event(self, event_info, pr, req_chatlevel=ChatLevels.CHATTY):
384394
"""
385395
Handle events of type pull_request with the action opened. Main action
386396
is to report for which architectures and repositories a bot instance is
@@ -420,10 +430,7 @@ def handle_pull_request_opened_event(self, event_info, pr):
420430

421431
# create comment to pull request
422432
repo_name = pr.base.repo.full_name
423-
gh = github.get_instance()
424-
repo = gh.get_repo(repo_name)
425-
pull_request = repo.get_pull(pr.number)
426-
issue_comment = pull_request.create_issue_comment(comment)
433+
issue_comment = create_comment(repo_name, pr.number, comment, req_chatlevel)
427434
return issue_comment
428435

429436
def handle_pull_request_event(self, event_info, log_file=None):
@@ -554,8 +561,9 @@ def handle_bot_command_show_config(self, event_info, bot_command):
554561
repo_name = event_info['raw_request_body']['repository']['full_name']
555562
pr_number = event_info['raw_request_body']['issue']['number']
556563
pr = gh.get_repo(repo_name).get_pull(pr_number)
557-
issue_comment = self.handle_pull_request_opened_event(event_info, pr)
558-
return f"\n - added comment {issue_comment.html_url} to show configuration"
564+
issue_comment = self.handle_pull_request_opened_event(event_info, pr, req_chatlevel=ChatLevels.MINIMAL)
565+
if issue_comment:
566+
return f"\n - added comment {issue_comment.html_url} to show configuration"
559567

560568
def handle_bot_command_status(self, event_info, bot_command):
561569
"""
@@ -571,7 +579,6 @@ def handle_bot_command_status(self, event_info, bot_command):
571579
PyGithub, not the github from the internal connections module)
572580
"""
573581
self.log("processing bot command 'status'")
574-
gh = github.get_instance()
575582
repo_name = event_info['raw_request_body']['repository']['full_name']
576583
pr_number = event_info['raw_request_body']['issue']['number']
577584
status_table = request_bot_build_issue_comments(repo_name, pr_number)
@@ -588,9 +595,7 @@ def handle_bot_command_status(self, event_info, bot_command):
588595
comment_status += f"{status_table['url'][x]}|"
589596

590597
self.log(f"Overview of finished builds: comment '{comment_status}'")
591-
repo = gh.get_repo(repo_name)
592-
pull_request = repo.get_pull(pr_number)
593-
issue_comment = pull_request.create_issue_comment(comment_status)
598+
issue_comment = create_comment(repo_name, pr_number, comment_status, ChatLevels.MINIMAL)
594599
return issue_comment
595600

596601
def start(self, app, port=3000):
@@ -669,12 +674,9 @@ def handle_pull_request_closed_event(self, event_info, pr):
669674
# 4) report move to pull request
670675

671676
repo_name = pr.base.repo.full_name
672-
gh = github.get_instance()
673-
repo = gh.get_repo(repo_name)
674-
pull_request = repo.get_pull(pr.number)
675677
clean_up_comment = self.cfg[config.SECTION_CLEAN_UP][config.CLEAN_UP_SETTING_MOVED_JOB_DIRS_COMMENT]
676678
moved_comment = clean_up_comment.format(job_dirs=job_dirs, trash_bin_dir=trash_bin_dir)
677-
issue_comment = pull_request.create_issue_comment(moved_comment)
679+
issue_comment = create_comment(repo_name, pr.number, moved_comment, ChatLevels.CHATTY)
678680
return issue_comment
679681

680682

0 commit comments

Comments
 (0)