Skip to content

Commit 6f3e97a

Browse files
authored
Merge pull request #378 from EESSI/develop
release v0.12.0
2 parents 29dc5e9 + c66083a commit 6f3e97a

15 files changed

Lines changed: 593 additions & 72 deletions

.coveragerc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[run]
2+
omit = tests/*

RELEASE_NOTES

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
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.12.0 (11 June 2026)
5+
--------------------------
6+
7+
This is a minor release of the EESSI build-and-deploy bot.
8+
9+
Bug fixes:
10+
* do not check export variables in `check_filters` function (#364)
11+
12+
Improvements:
13+
* increase coverage by unit tests (#365)
14+
* make parsing of `scontrol` output compatible with newer Slurm versions (#369)
15+
* With newer Slurm versions (25.11), some values can also contain whitespaces
16+
(e.g. for SubmitLine), making it complex to distinguish between keys and values.
17+
18+
Changes to 'app.cfg' settings (see README.md and app.cfg.example for details):
19+
* none
20+
21+
422
v0.11.0 (28 January 2026)
523
--------------------------
624

eessi_bot_job_manager.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,9 @@ def parse_scontrol_show_job_output(self, output):
272272
"""
273273
The output of 'scontrol --oneliner show job' is a list of key=value pairs
274274
separated by whitespaces.
275+
Note that with newer Slurm versions (25.11), some values can also contain whitespaces
276+
(e.g. for SubmitLine), making it complex to distinguish between keys and values.
277+
To solve this, we assume that all Slurm keys start with an uppercase letter.
275278
276279
Args:
277280
output (string): the output of the scontrol command
@@ -281,8 +284,20 @@ def parse_scontrol_show_job_output(self, output):
281284
"""
282285
job_info = {}
283286
stripped_output = output.strip()
284-
for pair in stripped_output.split():
285-
key, value = pair.split('=', 1)
287+
288+
# Match keys that start with uppercase and continue until '='
289+
key_pattern = re.compile(r'([A-Z][A-Za-z0-9:/_]*)=')
290+
291+
keys_matches = list(key_pattern.finditer(stripped_output))
292+
293+
for idx, key_match in enumerate(keys_matches):
294+
# The key is what actually got matched by the regex
295+
key = key_match.group(1)
296+
# The value starts where the key ends...
297+
value_start = key_match.end()
298+
# and it ends where the next key starts (or, if it's the last one, where the string ends)
299+
value_end = keys_matches[idx+1].start() if (idx + 1) < len(keys_matches) else len(stripped_output)
300+
value = stripped_output[value_start:value_end].strip()
286301
job_info[key] = value
287302

288303
return job_info

tests/conftest.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@
55
# EESSI software layer, see https://github.com/EESSI/software-layer
66
#
77
# author: Thomas Roeblitz (@trz42)
8+
# author: Sondre Bergsvaag Risanger (@sondrebr)
89
#
910
# license: GPLv2
1011
#
1112

13+
import os
14+
import shutil
15+
1216

1317
def pytest_configure(config):
1418
# register custom markers
@@ -24,3 +28,18 @@ def pytest_configure(config):
2428
config.addinivalue_line(
2529
"markers", "create_fails(bool): let function create_issue_comment return None"
2630
)
31+
32+
33+
def pytest_sessionstart():
34+
# Back up app.cfg if it exists
35+
if os.path.exists("app.cfg"):
36+
shutil.copyfile("app.cfg", "appbackup.cfg")
37+
38+
# Copy needed app.cfg from tests directory
39+
shutil.copyfile("tests/test_app.cfg", "app.cfg")
40+
41+
42+
def pytest_sessionfinish():
43+
# Restore backup if it exists
44+
if os.path.exists("appbackup.cfg"):
45+
shutil.copyfile("appbackup.cfg", "app.cfg")

tests/test_app.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@ running_job = job `{job_id}` is running
3636
[finished_job_comments]
3737

3838
[bot_control]
39+
command_permission = user01 second_user

tests/test_eessi_bot_job_manager.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,10 @@
1212
# license: GPLv2
1313
#
1414

15-
import shutil
16-
1715
from eessi_bot_job_manager import EESSIBotSoftwareLayerJobManager
1816

1917

2018
def test_determine_running_jobs():
21-
# copy needed app.cfg from tests directory
22-
shutil.copyfile("tests/test_app.cfg", "app.cfg")
23-
2419
job_manager = EESSIBotSoftwareLayerJobManager()
2520

2621
assert job_manager.determine_running_jobs({}) == []
@@ -135,3 +130,31 @@ def test_determine_finished_jobs():
135130
assert job_manager.determine_finished_jobs(known_jobs, current_jobs_all_jobs) == []
136131
assert job_manager.determine_finished_jobs(known_jobs, current_jobs_one_job) == ['1', '2']
137132
assert job_manager.determine_finished_jobs(known_jobs, {}) == ['0', '1', '2']
133+
134+
135+
def test_parse_scontrol_show_job_output():
136+
# Dummy output (shortened) from Slurm 25.11.3 for "scontrol show job <jobid>"
137+
scontrol_output = 'JobId=123 JobName=bot_test_job UserId=eessibot(12345) MCS_label=N/A EligibleTime=Unknown' \
138+
' AllocNode:Sid=my.node.name:123456 SubmitLine=/opt/slurm/25.11.3/bin/sbatch --hold' \
139+
' --time=10-0:0:0 --nodes=1 --exclusive --cpus-per-task=1 --job-name=bot_test_job ' \
140+
'/home/eessibot/job.slurm WorkDir=/jobs/2026.01/pr_123/event_123-456-789/run_000/riscv64/' \
141+
'generic/dev.eessi.io-riscv StdErr= StdIn=/dev/null StdOut=/jobs/2026.01/pr_123/' \
142+
'event_123-456-789/run_000/riscv64/generic/dev.eessi.io-riscv/slurm-123.out TresPerTask=cpu=1'
143+
job_manager = EESSIBotSoftwareLayerJobManager()
144+
job_info = job_manager.parse_scontrol_show_job_output(scontrol_output)
145+
job_info_expected = {
146+
'JobId': '123',
147+
'JobName': 'bot_test_job',
148+
'UserId': 'eessibot(12345)',
149+
'MCS_label': 'N/A',
150+
'EligibleTime': 'Unknown',
151+
'AllocNode:Sid': 'my.node.name:123456',
152+
'SubmitLine': '/opt/slurm/25.11.3/bin/sbatch --hold --time=10-0:0:0 --nodes=1 --exclusive --cpus-per-task=1 '
153+
'--job-name=bot_test_job /home/eessibot/job.slurm',
154+
'WorkDir': '/jobs/2026.01/pr_123/event_123-456-789/run_000/riscv64/generic/dev.eessi.io-riscv',
155+
'StdErr': '',
156+
'StdIn': '/dev/null',
157+
'StdOut': '/jobs/2026.01/pr_123/event_123-456-789/run_000/riscv64/generic/dev.eessi.io-riscv/slurm-123.out',
158+
'TresPerTask': 'cpu=1',
159+
}
160+
assert job_info == job_info_expected

0 commit comments

Comments
 (0)