Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,12 @@ jobdir = Job dir: `{symlink}`

`jobdir` is used as the fourth line in a comment to a PR when a new job has been created.

```ini
commit_sha = Commit SHA: `{commit_sha}`
```

`commit_sha` is used as the format string for the fifth line in a comment to a PR when a new job has been created. The `{commit_sha}` placeholder is replaced with the commit SHA from the cloned repository (i.e., the base branch HEAD at the time the job starts).

```ini
with_accelerator =  and accelerator `{accelerator}`
```
Expand Down
3 changes: 3 additions & 0 deletions app.cfg.example
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,9 @@ new_job_instance_repo = New job on instance `{app_name}` for repository `{repo_i
build_on_arch = Building on: `{on_arch}`{on_accelerator}
build_for_arch = Building for: `{for_arch}`{for_accelerator}
jobdir = Job dir: `{symlink}`
# commit_sha format for the 5th line in the initial PR comment; `{commit_sha}` is replaced with the commit SHA
# from the cloned repository (i.e., the base branch HEAD at the time the job starts).
commit_sha = Commit SHA: {commit_sha}
with_accelerator =  and accelerator `{accelerator}`
# initial_comment = New job on instance `{app_name}` for repository `{repo_id}`\nBuilding on: `{on_arch}`{on_accelerator}\nBuilding for: `{for_arch}`{for_accelerator}\nJob dir: `{symlink}` # no longer used

Expand Down
12 changes: 9 additions & 3 deletions eessi_bot_event_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,8 @@ def handle_bot_command_status(self, event_info, bot_command):

# Keep only the first entry for each 'for arch', as that is now the newest
status_table_last = {
'on arch': [], 'for arch': [], 'for repo': [], 'date': [], 'status': [], 'url': [], 'result': []
'on arch': [], 'for arch': [], 'for repo': [], 'date': [], 'status': [], 'url': [], 'result': [],
'commit sha': []
}
for x in range(0, len(sorted_table['date'])):
# Check if the current 'for arch' AND 'for repo' are already in the status_table_last. If not, add it
Expand Down Expand Up @@ -664,13 +665,18 @@ def handle_bot_command_status(self, event_info, bot_command):

comment_status = ''
comment_status += "\nThis is the status of all the `bot: build` commands:"
comment_status += "\n|on|for|repo|result|date|status|url|"
comment_status += "\n|----|----|----|------|----|------|---|"

# Build header
all_columns = ['on', 'for', 'repo', 'result', 'commit SHA', 'date', 'status', 'url']
comment_status += f"\n|{'|'.join(all_columns)}|"
comment_status += f"\n|{'|'.join(['----'] * len(all_columns))}|"

for x in range(0, len(status_table['date'])):
comment_status += f"\n|{status_table['on arch'][x]}|"
comment_status += f"{status_table['for arch'][x]}|"
comment_status += f"{status_table['for repo'][x]}|"
comment_status += f"{status_table['result'][x]}|"
comment_status += f"{status_table['commit sha'][x]}|"
comment_status += f"{status_table['date'][x]}|"
comment_status += f"{status_table['status'][x]}|"
comment_status += f"{status_table['url'][x]}|"
Expand Down
91 changes: 55 additions & 36 deletions tasks/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -972,13 +972,38 @@ def create_pr_comment(job, job_id, app_name, pr, symlink, build_params):
# get current date and time
dt = datetime.now(timezone.utc)

# Get commit SHA from cloned repo
commit_sha, _, _ = run_cmd('git rev-parse HEAD', 'Get commit SHA', job.working_dir, raise_on_error=False)
commit_sha = commit_sha.strip() if commit_sha else ''

# construct initial job comment
buildenv = config.read_config()[config.SECTION_BUILDENV]
job_handover_protocol = buildenv.get(config.BUILDENV_SETTING_JOB_HANDOVER_PROTOCOL)
new_job_instance_repo = submitted_job_comments_cfg[config.SUBMITTED_JOB_COMMENTS_SETTING_INSTANCE_REPO]
build_on_arch = submitted_job_comments_cfg[config.SUBMITTED_JOB_COMMENTS_SETTING_BUILD_ON_ARCH]
build_for_arch = submitted_job_comments_cfg[config.SUBMITTED_JOB_COMMENTS_SETTING_BUILD_FOR_ARCH]
jobdir = submitted_job_comments_cfg[config.SUBMITTED_JOB_COMMENTS_SETTING_JOBDIR]
commit_sha_fmt = submitted_job_comments_cfg.get(
config.SUBMITTED_JOB_COMMENTS_SETTING_COMMIT_SHA, 'Commit SHA: {commit_sha}')

# Build header lines (1-4)
header_lines = (f"{new_job_instance_repo}\n"

@casparvl casparvl Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI for the reviewer: taken out of the if-statement for deduplication, since this is the common part

f"{build_on_arch}\n"
f"{build_for_arch}\n"
f"{jobdir}\n").format(
app_name=app_name,
on_arch=on_arch,
for_arch=for_arch,
symlink=symlink,
repo_id=job.repo_id,
on_accelerator=on_accelerator_str,
for_accelerator=for_accelerator_str)

# Build line 5
commit_sha_line = ''
if commit_sha_fmt:
commit_sha_line += f"{commit_sha_fmt.format(commit_sha=commit_sha)}\n"

if job_handover_protocol == config.JOB_HANDOVER_PROTOCOL_DELAYED_BEGIN:
release_msg_string = config.SUBMITTED_JOB_COMMENTS_SETTING_AWAITS_RELEASE_DELAYED_BEGIN_MSG
release_comment_template = submitted_job_comments_cfg[release_msg_string]
Expand All @@ -987,44 +1012,24 @@ def create_pr_comment(job, job_id, app_name, pr, symlink, build_params):
poll_interval = int(job_manager_cfg.get(config.JOB_MANAGER_SETTING_POLL_INTERVAL))
delay_factor = float(buildenv.get(config.BUILDENV_SETTING_JOB_DELAY_BEGIN_FACTOR, 2))
eligible_in_seconds = int(poll_interval * delay_factor)
job_comment = (f"{new_job_instance_repo}\n"
f"{build_on_arch}\n"
f"{build_for_arch}\n"
f"{jobdir}\n"
f"|date|job status|comment|\n"
f"|----------|----------|------------------------|\n"
f"|{dt.strftime('%b %d %X %Z %Y')}|"
f"submitted|"
f"{release_comment_template}|").format(
app_name=app_name,
on_arch=on_arch,
for_arch=for_arch,
symlink=symlink,
repo_id=job.repo_id,
job_id=job_id,
delay_seconds=eligible_in_seconds,
on_accelerator=on_accelerator_str,
for_accelerator=for_accelerator_str)
table = (f"|date|job status|comment|\n"
f"|----------|----------|------------------------|\n"
f"|{dt.strftime('%b %d %X %Z %Y')}|"
f"submitted|"
f"{release_comment_template}|").format(
job_id=job_id,
delay_seconds=eligible_in_seconds)
job_comment = header_lines + commit_sha_line + table
else:
release_msg_string = config.SUBMITTED_JOB_COMMENTS_SETTING_AWAITS_RELEASE_HOLD_RELEASE_MSG
release_comment_template = submitted_job_comments_cfg[release_msg_string]
job_comment = (f"{new_job_instance_repo}\n"
f"{build_on_arch}\n"
f"{build_for_arch}\n"
f"{jobdir}\n"
f"|date|job status|comment|\n"
f"|----------|----------|------------------------|\n"
f"|{dt.strftime('%b %d %X %Z %Y')}|"
f"submitted|"
f"{release_comment_template}|").format(
app_name=app_name,
on_arch=on_arch,
for_arch=for_arch,
symlink=symlink,
repo_id=job.repo_id,
job_id=job_id,
on_accelerator=on_accelerator_str,
for_accelerator=for_accelerator_str)
table = (f"|date|job status|comment|\n"
f"|----------|----------|------------------------|\n"
f"|{dt.strftime('%b %d %X %Z %Y')}|"
f"submitted|"
f"{release_comment_template}|").format(
job_id=job_id)
job_comment = header_lines + commit_sha_line + table

# create comment to pull request
repo_name = pr.base.repo.full_name
Expand Down Expand Up @@ -1212,7 +1217,8 @@ def request_bot_build_issue_comments(repo_name, pr_number):
"""
fn = sys._getframe().f_code.co_name

status_table = {'on arch': [], 'for arch': [], 'for repo': [], 'date': [], 'status': [], 'url': [], 'result': []}
status_table = {'on arch': [], 'for arch': [], 'for repo': [], 'date': [], 'status': [], 'url': [],
'result': [], 'commit sha': []}
cfg = config.read_config()
github_section = cfg[config.SECTION_GITHUB]
api_timeout = int(github_section.get(config.GITHUB_SETTING_API_TIMEOUT, 10))
Expand Down Expand Up @@ -1342,6 +1348,18 @@ def request_bot_build_issue_comments(repo_name, pr_number):
msg += f"{for_arch_re.pattern}\n"
raise ValueError(msg)

# Extract commit SHA (line 5, index 4)
commit_sha = ''
commit_sha_fmt = submitted_job_comments_section.get(
config.SUBMITTED_JOB_COMMENTS_SETTING_COMMIT_SHA, 'Commit SHA: `{commit_sha}`')
if len(comment_body) >= 5:
commit_sha_re = template_to_regex(commit_sha_fmt)
commit_sha_match = re.match(commit_sha_re, comment_body[4])
if commit_sha_match:
commit_sha = commit_sha_match.group('commit_sha')
else:
commit_sha = ''

# get date, status, url and result from the markdown table
comment_table = comment['body'][comment['body'].find('|'):comment['body'].rfind('|')+1]

Expand Down Expand Up @@ -1397,6 +1415,7 @@ def request_bot_build_issue_comments(repo_name, pr_number):
status_table['status'].append(status)
status_table['url'].append(url)
status_table['result'].append(result)
status_table['commit sha'].append(commit_sha)

return status_table

Expand Down
4 changes: 4 additions & 0 deletions tests/test_app.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ new_job_instance_repo = New job on instance `{app_name}` for repository `{repo_i
build_on_arch = Building on: `{on_arch}`{on_accelerator}
build_for_arch = Building for: `{for_arch}`{for_accelerator}
jobdir = Job dir: `{symlink}`
commit_sha = Commit SHA: `{commit_sha}`
with_accelerator =  and accelerator `{accelerator}`

[new_job_comments]
Expand All @@ -37,3 +38,6 @@ running_job = job `{job_id}` is running

[bot_control]
command_permission = user01 second_user

[github]
api_timeout = 10
89 changes: 87 additions & 2 deletions tests/test_task_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
import filecmp
import os
import re
from unittest.mock import patch
from unittest.mock import Mock, patch

# Third party imports (anything installed into the local Python environment)
from collections import namedtuple
from datetime import datetime
import pytest

# Local application imports (anything from EESSI/eessi-bot-software-layer)
from tasks.build import Job, create_pr_comment
from tasks.build import Job, create_pr_comment, request_bot_build_issue_comments
from tools import run_cmd, run_subprocess
from tools.build_params import EESSIBotBuildParams
from tools.job_metadata import create_metadata_file, read_metadata_file
Expand Down Expand Up @@ -528,3 +528,88 @@ def test_create_read_metadata_file(mocked_github, tmp_path):
job_id5 = "555"
with pytest.raises(TypeError):
create_metadata_file(job5, job_id5, pr_comment)


@pytest.mark.repo_name("EESSI/software-layer")
@pytest.mark.pr_number(1)
def test_create_pr_comment_with_commit_sha(monkeypatch, mocked_github, tmp_path):
"""Tests that create_pr_comment includes commit SHA from cloned repo."""
import subprocess
monkeypatch.setattr('tools.pr_comments.github', mocked_github)

# Set up a git repo in tmp_path with a commit
subprocess.run(['git', 'init'], cwd=tmp_path, capture_output=True)
subprocess.run(['git', 'config', 'user.name', 'test'], cwd=tmp_path, capture_output=True)
subprocess.run(['git', 'config', 'user.email', 'test@test.com'], cwd=tmp_path, capture_output=True)
test_file = os.path.join(tmp_path, 'test.txt')
with open(test_file, 'w') as f:
f.write('test content')
subprocess.run(['git', 'add', '.'], cwd=tmp_path, capture_output=True)
subprocess.run(['git', 'commit', '-m', 'Initial commit'], cwd=tmp_path, capture_output=True)

ym = datetime.today().strftime('%Y.%m')
pr_number = 1
job = Job(tmp_path, "test/architecture", "EESSI", "--speed-up", ym, pr_number, "fpga/magic", "user01")
build_params = EESSIBotBuildParams("arch=amd/zen4,accel=nvidia/cc90")

job_id = "123"
app_name = "pytest"

repo_name = "EESSI/software-layer"
repo = mocked_github.get_repo(repo_name)
pr = repo.get_pull(pr_number)
symlink = "/symlink"
comment = create_pr_comment(job, job_id, app_name, pr, symlink, build_params)

# Get the actual commit SHA
result = subprocess.run(['git', 'rev-parse', 'HEAD'], cwd=tmp_path, capture_output=True, text=True)
expected_sha = result.stdout.strip()

assert comment.id == 1
assert f"Commit SHA: `{expected_sha}`" in comment.body


@pytest.mark.repo_name("EESSI/software-layer")
@pytest.mark.pr_number(1)
def test_request_bot_build_issue_comments(monkeypatch):
"""Tests that request_bot_build_issue_comments extracts commit SHA."""
from tools import config as build_config
original_read_config = build_config.read_config

def mock_read_config(path='app.cfg'):
cfg = original_read_config(path)
return cfg

monkeypatch.setattr('tasks.build.config.read_config', mock_read_config)

# Mock the GitHub token
token_mock = Mock()
token_mock.token = 'mock-token'
monkeypatch.setattr('tasks.build.github.token', lambda: token_mock)
monkeypatch.setattr('tasks.build.github.get_instance', lambda: Mock())

# Mock the response from the GitHub API
comment_body = "\n".join([
"New job on instance `pytest` for repository `EESSI/software-layer`",
"Building on: `x86_64/generic`",
"Building for: `x86_64/generic`",
"Job dir: `symlink`",
"Commit SHA: `abc123`",
"|date|job status|comment|",
"|----------|----------|------------------------|",
"|Jan 01 00:00:00 UTC 2025|finished|SUCCESS|",
])
response_mock = Mock()
response_mock.json.return_value = [{'body': comment_body, 'html_url': 'https://example.com'}]
response_mock.links = {}
response_mock.headers = {
'X-RateLimit-Reset': '0',
'X-RateLimit-Limit': '5000',
'X-RateLimit-Remaining': '4999',
}
monkeypatch.setattr('tasks.build.requests.get', lambda *args, **kwargs: response_mock)

status_table = request_bot_build_issue_comments('EESSI/software-layer', 1)

assert status_table['commit sha'] == ['abc123']
assert status_table['result'] == [':grin: SUCCESS']
1 change: 1 addition & 0 deletions tools/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
SUBMITTED_JOB_COMMENTS_SETTING_JOBDIR = 'jobdir'
SUBMITTED_JOB_COMMENTS_SETTING_INITIAL_COMMENT = 'initial_comment'
SUBMITTED_JOB_COMMENTS_SETTING_WITH_ACCELERATOR = 'with_accelerator'
SUBMITTED_JOB_COMMENTS_SETTING_COMMIT_SHA = 'commit_sha'

SECTION_CLEAN_UP = 'clean_up'
CLEAN_UP_SETTING_TRASH_BIN_ROOT_DIR = 'trash_bin_dir'
Expand Down
Loading