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
97 changes: 97 additions & 0 deletions .github/scripts/retrieve_device_classes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""
This module contains helper methods to retrieve a list of all devices in a directory.
It does so by recursively traversing the directory and checking if the file contains a
class that inherits from the Device class.
"""

import importlib.util
import os

from ophyd import Device


def get_devices(repo_name: str, ignore: str = "") -> dict:
"""
Get all devices in a directory.

Args:
directory (str): The directory to search for devices.
ignore (str): A comma-separated list of module names to ignore.

Returns:
list: A list of all devices in the directory.
"""
devices = {}

anchor_module = importlib.import_module(f"{repo_name}.devices")
directory = os.path.dirname(anchor_module.__file__)

for root, _, files in os.walk(directory):
for file in files:
if not file.endswith(".py") or file.startswith("__"):
continue

path = os.path.join(root, file)
subs = os.path.dirname(os.path.relpath(path, directory)).split("/")
if len(subs) == 1 and not subs[0]:
module_name = file.split(".")[0]
else:
module_name = ".".join(subs + [file.split(".")[0]])

if module_name in ignore.split(","):
continue
module = importlib.import_module(f"{repo_name}.devices.{module_name}")

for name in dir(module):
obj = getattr(module, name)
if not hasattr(obj, "__module__") or obj.__module__ != module.__name__:
continue
if isinstance(obj, type) and issubclass(obj, Device) and obj is not Device:
devices[obj.__name__] = obj

return dict(sorted(devices.items(), key=lambda x: x[0].lower()))


def write_device_list(devices: dict, file_name: str, repo_name: str, append=False):
"""
Write the list of devices to a file.

Args:
devices (list): A list of devices.
file_out (str): The file to write the devices to.
Comment thread
wakonig marked this conversation as resolved.
repo_name (str): The repository name for linking to the source code.
append (bool): Whether to append to the file or overwrite it.
"""
if not append or not os.path.exists(file_name):
with open(file_name, "w", encoding="utf-8") as output_file:
output_file.write("// This file was autogenerated. Do not edit it manually.\n")
output_file.write("## Device List\n")

with open(file_name, "a", encoding="utf-8") as output_file:
output_file.write(f"### {repo_name} \n")
output_file.write("| Device | Documentation | Module |\n")
output_file.write("| :----- | :------------- | :------ |\n")
for dev, cls in devices.items():
doc = cls.__doc__
doc = "" if doc is None else doc.replace("\n", "<br>")
output_file.write(
f"| {dev} | {doc} | [{cls.__module__}](https://github.com/bec-project/{repo_name}/tree/main/{cls.__module__.replace('.', '/')}.py) |\n"
Comment thread
wakonig marked this conversation as resolved.
)


if __name__ == "__main__":
import argparse

parser = argparse.ArgumentParser(description="Retrieve a list of devices in a directory.")
parser.add_argument("repo", type=str, help="The repository to link to.")
parser.add_argument("output", type=str, help="The file to write the devices to.")
parser.add_argument(
"--append", action="store_true", help="Append to the file instead of overwriting it."
)
parser.add_argument(
"--ignore", type=str, help="Ignore the specified modules (comma-separated).", default=""
)

args = parser.parse_args()
devs = get_devices(args.repo, ignore=args.ignore)
write_device_list(devs, args.output, args.repo, append=args.append)
98 changes: 98 additions & 0 deletions .github/workflows/device-list-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: Update device list

on:
push:
branches:
- main

jobs:
device_list_update:
runs-on: ubuntu-latest
permissions:
contents: read
Comment thread
wakonig marked this conversation as resolved.
concurrency:
group: device-list-update-${{ github.ref_name }}
cancel-in-progress: true

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # required for git diff / reset
ssh-key: ${{ secrets.CI_DEPLOY_SSH_KEY }}
ssh-known-hosts: ${{ secrets.CI_DEPLOY_SSH_KNOWN_HOSTS }}

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Setup | Force release branch to be at workflow sha
run: |
git reset --hard ${{ github.sha }}

- name: Evaluate | Verify upstream has NOT changed
shell: bash
run: |
set +o pipefail

UPSTREAM_BRANCH_NAME="$(git status -sb | head -n 1 | cut -d' ' -f2 | grep -E '\.{3}' | cut -d'.' -f4)"
printf '%s\n' "Upstream branch name: $UPSTREAM_BRANCH_NAME"

set -o pipefail

if [ -z "$UPSTREAM_BRANCH_NAME" ]; then
printf >&2 '%s\n' "::error::Unable to determine upstream branch name!"
exit 1
fi

git fetch "${UPSTREAM_BRANCH_NAME%%/*}"

if ! UPSTREAM_SHA="$(git rev-parse "$UPSTREAM_BRANCH_NAME")"; then
printf >&2 '%s\n' "::error::Unable to determine upstream branch sha!"
exit 1
fi

HEAD_SHA="$(git rev-parse HEAD)"

if [ "$HEAD_SHA" != "$UPSTREAM_SHA" ]; then
printf >&2 '%s\n' "[HEAD SHA] $HEAD_SHA != $UPSTREAM_SHA [UPSTREAM SHA]"
printf >&2 '%s\n' "::error::Upstream has changed, aborting release..."
exit 1
fi

printf '%s\n' "Verified upstream branch has not changed, continuing..."

- name: Install Python dependencies
run: |
pip install .

- name: Generate device list
run: |
python .github/scripts/retrieve_device_classes.py \
"ophyd_devices" \
"./ophyd_devices/devices/device_list.md" \
--ignore areadetector.plugins

- name: Commit and push if device list changed
run: |
FILE="./ophyd_devices/devices/device_list.md"

if [ -f "$FILE" ]; then
git add "$FILE"

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

if ! git diff-index --quiet HEAD --; then
git commit -m "docs: Update device list"

git push origin "${{ github.ref_name }}"

echo "Device list updated"
else
echo "No changes detected"
fi
else
echo "Device list file not found"
fi
7 changes: 1 addition & 6 deletions .github/workflows/semantic_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ on:
permissions:
contents: read



jobs:
release:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -39,7 +37,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
python-version: "3.11"

- name: Setup | Force release branch to be at workflow sha
run: |
Expand All @@ -49,9 +47,6 @@ jobs:
# the upstream branch while this workflow was running. This is important
# because we are committing a version change (--commit). You may omit this step
# if you have 'commit: false' in your configuration.
#
# You may consider moving this to a repo script and call it from this step instead
# of writing it in-line.
shell: bash
run: |
set +o pipefail
Expand Down
Loading