Skip to content

Commit

Permalink
Merge branch 'easybuilders:develop' into easybuild-easyconfigs-Archiv…
Browse files Browse the repository at this point in the history
…e-Zip
  • Loading branch information
julianmorillo authored Nov 29, 2024
2 parents 07343dd + f3bd10b commit c7b116d
Show file tree
Hide file tree
Showing 419 changed files with 21,331 additions and 169 deletions.
182 changes: 182 additions & 0 deletions .github/workflows/tagbot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# NOTE: In order to write comment and edit labels, this script requires workflows with write permissions.
# It should not use any untrusted third party code, or any code checked into the repository itself
# as that could indirectly grant PRs the ability to edit labels and comments on PRs.

import os
import git
import requests
import json
from pathlib import Path


def get_first_commit_date(repo, file_path):
commits = list(repo.iter_commits(paths=file_path))
if commits:
return commits[-1].committed_date
else:
raise ValueError(f'{file_path} has no commit info, this should not happen')


def sort_by_added_date(repo, file_paths):
files_with_dates = [(get_first_commit_date(repo, file_path), file_path) for file_path in file_paths]
sorted_files = sorted(files_with_dates, reverse=True)
return [file for date, file in sorted_files]


def similar_easyconfigs(repo, new_file):
possible_neighbours = [x for x in new_file.parent.glob('*.eb') if x != new_file]
return sort_by_added_date(repo, possible_neighbours)


def pr_ecs(pr_diff):
new_ecs = []
changed_ecs = []
for item in pr_diff:
if item.a_path.endswith('.eb'):
if item.change_type == 'A':
new_ecs.append(Path(item.a_path))
else:
changed_ecs.append(Path(item.a_path))
return new_ecs, changed_ecs


GITHUB_API_URL = 'https://api.github.com'
event_path = os.getenv('GITHUB_EVENT_PATH')
token = os.getenv('GH_TOKEN')
repo = os.getenv('GITHUB_REPOSITORY')
base_branch_name = os.getenv('GITHUB_BASE_REF')

with open(event_path) as f:
data = json.load(f)

pr_number = data['pull_request']['number']
# Can't rely on merge_commit_sha for pull_request_target as it might be outdated
# merge_commit_sha = data['pull_request']['merge_commit_sha']

print("PR number:", pr_number)
print("Base branch name:", base_branch_name)

# Change into "pr" checkout directory to allow diffs and glob to work on the same content
os.chdir('pr')
gitrepo = git.Repo('.')

target_commit = gitrepo.commit('origin/' + base_branch_name)
print("Target commit ref:", target_commit)
merge_commit = gitrepo.head.commit
print("Merge commit:", merge_commit)
pr_diff = target_commit.diff(merge_commit)

new_ecs, changed_ecs = pr_ecs(pr_diff)
modified_workflow = any(item.a_path.startswith('.github/workflows/') for item in pr_diff)


print("Changed ECs:", ', '.join(str(p) for p in changed_ecs))
print("Newly added ECs:", ', '.join(str(p) for p in new_ecs))
print("Modified workflow:", modified_workflow)

new_software = 0
updated_software = 0
to_diff = dict()
for new_file in new_ecs:
neighbours = similar_easyconfigs(gitrepo, new_file)
print(f"Found {len(neighbours)} neighbours for {new_file}")
if neighbours:
updated_software += 1
to_diff[new_file] = neighbours
else:
new_software += 1

print(f"Generating comment for {len(to_diff)} updates softwares")
# Limit comment size for large PRs:
if len(to_diff) > 20: # Too much, either bad PR or some broad change. Not diffing.
max_diffs_per_software = 0
elif len(to_diff) > 10:
max_diffs_per_software = 1
elif len(to_diff) > 5:
max_diffs_per_software = 2
else:
max_diffs_per_software = 3

comment = ''
if max_diffs_per_software > 0:
for new_file, neighbours in to_diff.items():
compare_neighbours = neighbours[:max_diffs_per_software]
if compare_neighbours:
print(f"Diffs for {new_file}")
comment += f'#### Updated software `{new_file.name}`\n\n'

for neighbour in compare_neighbours:
print(f"against {neighbour}")
comment += '<details>\n'
comment += f'<summary>Diff against <code>{neighbour.name}</code></summary>\n\n'
comment += f'[{neighbour}](https://github.com/{repo}/blob/{base_branch_name}/{neighbour})\n\n'
comment += '```diff\n'
comment += gitrepo.git.diff(f'HEAD:{neighbour}', f'HEAD:{new_file}')
comment += '\n```\n</details>\n\n'

print("Adjusting labels")
current_labels = [label['name'] for label in data['pull_request']['labels']]

label_checks = [(changed_ecs, 'change'),
(new_software, 'new'),
(updated_software, 'update'),
(modified_workflow, 'workflow')]

labels_add = []
labels_del = []
for condition, label in label_checks:
if condition and label not in current_labels:
labels_add.append(label)
elif not condition and label in current_labels:
labels_del.append(label)

url = f"{GITHUB_API_URL}/repos/{repo}/issues/{pr_number}/labels"

headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"X-GitHub-Api-Version": "2022-11-28",
}

if labels_add:
print(f"Setting labels: {labels_add} at {url}")
response = requests.post(url, headers=headers, json={"labels": labels_add})
if response.status_code == 200:
print(f"Labels {labels_add} added successfully.")
else:
print(f"Failed to add labels: {response.status_code}, {response.text}")

for label in labels_del:
print(f"Removing label: {label} at {url}")
response = requests.delete(f'{url}/{label}', headers=headers)
if response.status_code == 200:
print(f"Label {label} removed successfully.")
else:
print(f"Failed to delete label: {response.status_code}, {response.text}")

# Write comment with diff
if updated_software:
# Search for comment by bot to potentially replace
url = f"{GITHUB_API_URL}/repos/{repo}/issues/{pr_number}/comments"
response = requests.get(url, headers=headers)
comment_id = None
for existing_comment in response.json():
if existing_comment["user"]["login"] == "github-actions[bot]": # Bot username in GitHub Actions
comment_id = existing_comment["id"]

if comment_id:
# Update existing comment
url = f"{GITHUB_API_URL}/repos/{repo}/issues/comments/{comment_id}"
response = requests.patch(url, headers=headers, json={"body": comment})
if response.status_code == 200:
print("Comment updated successfully.")
else:
print(f"Failed to update comment: {response.status_code}, {response.text}")
else:
# Post a new comment
url = f"{GITHUB_API_URL}/repos/{repo}/issues/{pr_number}/comments"
response = requests.post(url, headers=headers, json={"body": comment})
if response.status_code == 201:
print("Comment posted successfully.")
else:
print(f"Failed to post comment: {response.status_code}, {response.text}")
54 changes: 54 additions & 0 deletions .github/workflows/tagbot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Tagbot
on: [pull_request_target]

concurrency:
group: "${{ github.workflow }}-${{ github.event.pull_request.number }}"
cancel-in-progress: true

jobs:
tagbot:
# Note: can't rely on github.event.pull_request.merge_commit_sha because pull_request_target
# does not wait for github mergability check, and the value is outdated.
# Instead we merge manually in a temporary subdir "pr"
runs-on: ubuntu-24.04
permissions:
pull-requests: write
steps:
- name: Checkout base branch for workflow scripts
uses: actions/checkout@v4

- name: Checkout PR for computing diff into "pr" subdirectory
uses: actions/checkout@v4
with:
ref: "${{ github.event.pull_request.head.sha }}"
path: 'pr'
fetch-depth: 0

- name: Attempt test merge
id: merge
run: |
git config user.name "github-workflow"
git config user.email "[email protected]"
git merge --no-edit --no-ff origin/${{ github.event.pull_request.base.ref }}
continue-on-error: true
working-directory: pr

- name: Abort if merge failed
if: steps.merge.outcome == 'failure'
run: |
echo "Merge conflict detected, failing job."
exit 1
- name: set up Python
uses: actions/setup-python@v5
with:
python-version: 3.12

- name: Get packages
run: pip install gitpython requests

- name: Tag and comment
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: python .github/workflows/tagbot.py

29 changes: 29 additions & 0 deletions easybuild/easyconfigs/a/AOCC/AOCC-4.2.0-GCCcore-13.3.0.eb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name = 'AOCC'
version = '4.2.0'

homepage = 'https://developer.amd.com/amd-aocc/'
description = """AOCC is a high-performance x86 CPU compiler for C, C++, and Fortran programming languages.
It supports target-dependent and target-independent optimizations. It is highly optimized for x86 targets,
especially for AMD “Zen”-based processors giving a performance edge for time critical HPC and other
applications over other compilers."""

# Clang also depends on libstdc++ during runtime, but this dependency is
# already specified as the toolchain.
toolchain = {'name': 'GCCcore', 'version': '13.3.0'}

source_urls = [
'https://download.amd.com/developer/eula/%(namelower)s/%(namelower)s-%(version_major)s-%(version_minor)s'
]
sources = ['%(namelower)s-compiler-%(version)s.tar']
checksums = ['ed5a560ec745b24dc0685ccdcbde914843fb2f2dfbfce1ba592de4ffbce1ccab']

dependencies = [
('binutils', '2.42'),
('ncurses', '6.5'),
('zlib', '1.3.1'),
('libxml2', '2.12.7'),
]

clangversion = '16.0.3'

moduleclass = 'compiler'
24 changes: 24 additions & 0 deletions easybuild/easyconfigs/a/AOCC/AOCC-5.0.0-GCCcore-14.2.0.eb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name = 'AOCC'
version = '5.0.0'

homepage = 'https://developer.amd.com/amd-aocc/'
description = "AMD Optimized C/C++ & Fortran compilers (AOCC) based on LLVM 13.0"

# Clang also depends on libstdc++ during runtime, but this dependency is
# already specified as the toolchain.
toolchain = {'name': 'GCCcore', 'version': '14.2.0'}

source_urls = ['https://download.amd.com/developer/eula/aocc/aocc-5-0/']
sources = ['aocc-compiler-%(version)s.tar']
checksums = ['966fac2d2c759e9de6e969c10ada7a7b306c113f7f1e07ea376829ec86380daa']

clangversion = '17.0.6'

dependencies = [
('binutils', '2.42'),
('ncurses', '6.5'),
('zlib', '1.3.1'),
('libxml2', '2.13.4'),
]

moduleclass = 'compiler'
48 changes: 48 additions & 0 deletions easybuild/easyconfigs/a/ASE/ASE-3.23.0-gfbf-2023b.eb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
easyblock = 'PythonBundle'

name = 'ASE'
version = '3.23.0'

homepage = 'https://wiki.fysik.dtu.dk/ase'
description = """ASE is a python package providing an open source Atomic Simulation Environment
in the Python scripting language.
From version 3.20.1 we also include the ase-ext package, it contains optional reimplementations
in C of functions in ASE. ASE uses it automatically when installed."""

toolchain = {'name': 'gfbf', 'version': '2023b'}

dependencies = [
('Python', '3.11.5'),
('Python-bundle-PyPI', '2023.10'),
('SciPy-bundle', '2023.11'),
('Flask', '3.0.0'),
('matplotlib', '3.8.2'),
('Tkinter', '%(pyver)s'), # Needed by GUI of ASE
('spglib-python', '2.5.0'), # optional
]

use_pip = True
sanity_pip_check = True

exts_list = [
('pytest-mock', '3.14.0', {
'checksums': ['2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0'],
}),
('ase', version, {
'checksums': ['91a2aa31d89bd90b0efdfe4a7e84264f32828b2abfc9f38e65e041ad76fec8ae'],
}),
('ase-ext', '20.9.0', {
'checksums': ['a348b0e42cf9fdd11f04b3df002b0bf150002c8df2698ff08d3c8fc7a1223aed'],
}),
]

sanity_check_paths = {
'files': ['bin/ase'],
'dirs': ['lib/python%(pyshortver)s/site-packages'],
}

# make sure Tkinter is available, otherwise 'ase gui' will not work
sanity_check_commands = ["python -c 'import tkinter' "]

moduleclass = 'chem'
48 changes: 48 additions & 0 deletions easybuild/easyconfigs/a/ASE/ASE-3.23.0-gfbf-2024a.eb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
easyblock = 'PythonBundle'

name = 'ASE'
version = '3.23.0'

homepage = 'https://wiki.fysik.dtu.dk/ase'
description = """ASE is a python package providing an open source Atomic Simulation Environment
in the Python scripting language.
From version 3.20.1 we also include the ase-ext package, it contains optional reimplementations
in C of functions in ASE. ASE uses it automatically when installed."""

toolchain = {'name': 'gfbf', 'version': '2024a'}

dependencies = [
('Python', '3.12.3'),
('Python-bundle-PyPI', '2024.06'),
('SciPy-bundle', '2024.05'),
('Flask', '3.0.3'),
('matplotlib', '3.9.2'),
('Tkinter', '%(pyver)s'), # Needed by GUI of ASE
('spglib-python', '2.5.0'), # optional
]

use_pip = True
sanity_pip_check = True

exts_list = [
('pytest-mock', '3.14.0', {
'checksums': ['2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0'],
}),
('ase', version, {
'checksums': ['91a2aa31d89bd90b0efdfe4a7e84264f32828b2abfc9f38e65e041ad76fec8ae'],
}),
('ase-ext', '20.9.0', {
'checksums': ['a348b0e42cf9fdd11f04b3df002b0bf150002c8df2698ff08d3c8fc7a1223aed'],
}),
]

sanity_check_paths = {
'files': ['bin/ase'],
'dirs': ['lib/python%(pyshortver)s/site-packages'],
}

# make sure Tkinter is available, otherwise 'ase gui' will not work
sanity_check_commands = ["python -c 'import tkinter' "]

moduleclass = 'chem'
Loading

0 comments on commit c7b116d

Please sign in to comment.