Skip to content
Open
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
16 changes: 13 additions & 3 deletions .github/workflows/assigner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ jobs:
with:
python-version: 3.12

- name: Fetch west.yml from pull request
- name: Fetch west.yml/Maintainer.yml from pull request
if: >
github.event_name == 'pull_request_target'
run: |
git fetch origin pull/${{ github.event.pull_request.number }}/merge
git show FETCH_HEAD:west.yml > pr_west.yml
git show FETCH_HEAD:MAINTAINERS.yml > pr_MAINTAINERS.yml

- name: west setup
if: >
Expand All @@ -62,7 +63,7 @@ jobs:
FLAGS+=" -r ${{ github.event.repository.name }}"
FLAGS+=" -M MAINTAINERS.yml"
if [ "${{ github.event_name }}" = "pull_request_target" ]; then
FLAGS+=" -P ${{ github.event.pull_request.number }} --updated-manifest pr_west.yml"
FLAGS+=" -P ${{ github.event.pull_request.number }} --updated-manifest pr_west.yml --updated-maintainer-file pr_MAINTAINERS.yml"
elif [ "${{ github.event_name }}" = "issues" ]; then
FLAGS+=" -I ${{ github.event.issue.number }}"
elif [ "${{ github.event_name }}" = "schedule" ]; then
Expand All @@ -71,4 +72,13 @@ jobs:
echo "Unknown event: ${{ github.event_name }}"
exit 1
fi
python3 scripts/set_assignees.py $FLAGS
python3 scripts/ci/set_assignees.py $FLAGS

- name: Check maintainer file changes
if: >
github.event_name == 'pull_request_target'
env:
GITHUB_TOKEN: ${{ secrets.ZB_PR_ASSIGNER_GITHUB_TOKEN }}
run: |
python ./scripts/ci/check_maintainer_changes.py \
--repo zephyrproject-rtos/zephyr MAINTAINERS.yml pr_MAINTAINERS.yml
43 changes: 0 additions & 43 deletions .github/workflows/maintainer_check.yml

This file was deleted.

1 change: 1 addition & 0 deletions .ruff-excludes.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,7 @@ exclude = [
"./scripts/ci/coverage/coverage_analysis.py",
"./scripts/ci/errno.py",
"./scripts/ci/guideline_check.py",
"./scripts/ci/set_assignees.py",
"./scripts/ci/stats/merged_prs.py",
"./scripts/ci/test_plan.py",
"./scripts/ci/twister_report_analyzer.py",
Expand Down
42 changes: 37 additions & 5 deletions MAINTAINERS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
# labels:
# List of GitHub labels to add to pull requests that modify the area.
#
# tests:
# List of test identifiers to be run when this area is affected by a PR.
#
# files:
# List of paths and/or glob patterns giving the files in the area,
# relative to the root directory.
Expand Down Expand Up @@ -60,6 +63,22 @@
# Like 'files-regex', but any matching files will be excluded from the
# area.
#
# file-groups:
# A list of groups of files that are treated as a single unit.
# This is useful for areas where different collaborators are responsible for
# different parts of the area.
# Each group should have the following structure:
# - name: <group name>
# collaborators:
# - <GitHub handle>
# - <GitHub handle>
# files:
# - <file path>
# - <file path>
# files-regex:
# - <regex pattern>
# - <regex pattern>
#
# description: >-
# Plain-English description. Describe what the system is about, from an
# outsider's perspective.
Expand Down Expand Up @@ -938,17 +957,30 @@ Continuous Integration:
maintainers:
- stephanosio
- nashif
collaborators:
- fabiobaltieri
- kartben
file-groups:
- name: CI scripts
collaborators:
- kartben
files:
- scripts/ci/
- name: Gitlint
collaborators:
- fabiobaltieri
files:
- scripts/gitlint/
- name: Assignee setter
collaborators:
- kartben
files:
- scripts/ci/set_assignees.py
files:
- .github/
- scripts/requirements-actions.*
- scripts/ci/
- scripts/make_bugs_pickle.py
- .checkpatch.conf
- scripts/gitlint/
- scripts/set_assignees.py
- scripts/ci/set_assignees.py
labels:
- "area: Continuous Integration"

Expand Down Expand Up @@ -3264,7 +3296,7 @@ MAINTAINERS file:
files:
- MAINTAINERS.yml
- scripts/get_maintainer.py
- scripts/set_assignees.py
- scripts/ci/set_assignees.py
- scripts/check_maintainers.py
labels:
- "area: MAINTAINER File"
Expand Down
141 changes: 139 additions & 2 deletions scripts/set_assignees.py → scripts/ci/set_assignees.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/usr/bin/env python3



# Copyright (c) 2022 Intel Corp.
# SPDX-License-Identifier: Apache-2.0

Expand All @@ -9,16 +11,24 @@
import sys
import time
from collections import defaultdict
from pathlib import Path

import yaml
from github import Auth, Github, GithubException
from github.GithubException import UnknownObjectException
from west.manifest import Manifest, ManifestProject

TOP_DIR = os.path.join(os.path.dirname(__file__))
sys.path.insert(0, os.path.join(TOP_DIR, "scripts"))
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from get_maintainer import Maintainers # noqa: E402

zephyr_base = os.getenv('ZEPHYR_BASE', os.path.join(TOP_DIR, '..'))
ZEPHYR_BASE = os.environ.get('ZEPHYR_BASE')
if ZEPHYR_BASE:
ZEPHYR_BASE = Path(ZEPHYR_BASE)
else:
ZEPHYR_BASE = Path(__file__).resolve().parents[2]
# Propagate this decision to child processes.
os.environ['ZEPHYR_BASE'] = str(ZEPHYR_BASE)


def log(s):
Expand Down Expand Up @@ -71,10 +81,22 @@
help="Updated manifest file to compare against current west.yml",
)

parser.add_argument(
"--updated-maintainer-file",
default=None,
help="Updated maintainer file to compare against current MAINTAINERS.yml",
)

parser.add_argument("-v", "--verbose", action="count", default=0, help="Verbose Output")

args = parser.parse_args()

def load_areas(filename: str):
with open(filename) as f:
doc = yaml.safe_load(f)
return {
k: v for k, v in doc.items() if isinstance(v, dict) and ("files" in v or "files-regex" in v)
}

def process_manifest(old_manifest_file):
log("Processing manifest changes")
Expand Down Expand Up @@ -104,6 +126,93 @@
log(f'manifest areas: {areas}')
return areas

def set_or_empty(d, key):
return set(d.get(key, []) or [])

def compare_areas(old, new, repo_fullname=None, token=None):
old_areas = set(old.keys())
new_areas = set(new.keys())

changed_areas = set()
added_areas = new_areas - old_areas
removed_areas = old_areas - new_areas
common_areas = old_areas & new_areas

print("=== Areas Added ===")
for area in sorted(added_areas):
print(f"+ {area}")

print("\n=== Areas Removed ===")
for area in sorted(removed_areas):
print(f"- {area}")

print("\n=== Area Changes ===")
for area in sorted(common_areas):
changes = []
old_entry = old[area]
new_entry = new[area]

# Compare maintainers
old_maint = set_or_empty(old_entry, "maintainers")
new_maint = set_or_empty(new_entry, "maintainers")
added_maint = new_maint - old_maint
removed_maint = old_maint - new_maint
if added_maint:
changes.append(f" Maintainers added: {', '.join(sorted(added_maint))}")
if removed_maint:
changes.append(f" Maintainers removed: {', '.join(sorted(removed_maint))}")

# Compare collaborators
old_collab = set_or_empty(old_entry, "collaborators")
new_collab = set_or_empty(new_entry, "collaborators")
added_collab = new_collab - old_collab
removed_collab = old_collab - new_collab
if added_collab:
changes.append(f" Collaborators added: {', '.join(sorted(added_collab))}")
if removed_collab:
changes.append(f" Collaborators removed: {', '.join(sorted(removed_collab))}")

# Compare status
old_status = old_entry.get("status")
new_status = new_entry.get("status")
if old_status != new_status:
changes.append(f" Status changed: {old_status} -> {new_status}")

# Compare labels
old_labels = set_or_empty(old_entry, "labels")
new_labels = set_or_empty(new_entry, "labels")
added_labels = new_labels - old_labels
removed_labels = old_labels - new_labels
if added_labels:
changes.append(f" Labels added: {', '.join(sorted(added_labels))}")
if removed_labels:
changes.append(f" Labels removed: {', '.join(sorted(removed_labels))}")

# Compare files
old_files = set_or_empty(old_entry, "files")
new_files = set_or_empty(new_entry, "files")
added_files = new_files - old_files
removed_files = old_files - new_files
if added_files:
changes.append(f" Files added: {', '.join(sorted(added_files))}")
if removed_files:
changes.append(f" Files removed: {', '.join(sorted(removed_files))}")

# Compare files-regex
old_regex = set_or_empty(old_entry, "files-regex")
new_regex = set_or_empty(new_entry, "files-regex")
added_regex = new_regex - old_regex
removed_regex = old_regex - new_regex
if added_regex:
changes.append(f" files-regex added: {', '.join(sorted(added_regex))}")
if removed_regex:
changes.append(f" files-regex removed: {', '.join(sorted(removed_regex))}")

if changes:
changed_areas.add(area)
print(f"area changed: {area}")

return added_areas | removed_areas | changed_areas

def process_pr(gh, maintainer_file, number):
gh_repo = gh.get_repo(f"{args.org}/{args.repo}")
Expand All @@ -128,6 +237,8 @@
# areas where assignment happens if only said areas are affected
meta_areas = ['Release Notes', 'Documentation', 'Samples', 'Tests']

collab_per_path = []
additional_reviews = set()
for changed_file in fn:
num_files += 1
log(f"file: {changed_file.filename}")
Expand All @@ -139,11 +250,30 @@
continue
parsed_areas = process_manifest(old_manifest_file=args.updated_manifest)
for _area in parsed_areas:
collab_per_path.extend(_area.get_collaborators_for_path(changed_file))
area_match = maintainer_file.name2areas(_area)
if area_match:
areas.extend(area_match)
elif changed_file.filename in ['MAINTAINERS.yml']:
areas = maintainer_file.path2areas(changed_file.filename)
if args.updated_maintainer_file:
log(
"No updated maintainer file, cannot process MAINTAINERS.yml changes, skipping..."

Check failure on line 261 in scripts/ci/set_assignees.py

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

Python lint error (E501) see https://docs.astral.sh/ruff/rules/line-too-long

/home/runner/work/zephyr-testing/zephyr-testing/scripts/ci/set_assignees.py:261 Line too long (101 > 100)
)

old_areas = load_areas(args.updated_maintainer_file)
new_areas = load_areas('MAINTAINERS.yml')
changed_areas = compare_areas(old_areas, new_areas)
for _area in changed_areas:
area_match = maintainer_file.name2areas(_area)
if area_match:
# get list of maintainers for changed area
additional_reviews.update(maintainer_file.areas[_area].maintainers)
log(f"MAINTAINERS.yml changed, adding reviewrs: {additional_reviews}")
else:
areas = maintainer_file.path2areas(changed_file.filename)
for _area in areas:
collab_per_path.extend(_area.get_collaborators_for_path(changed_file))

log(f"areas for {changed_file}: {areas}")

Expand Down Expand Up @@ -173,6 +303,9 @@
if 'Platform' in area.name:
is_instance = True

for _area in sorted_areas:
collab_per_path.extend(_area.get_collaborators_for_path(changed_file))

area_counter = dict(sorted(area_counter.items(), key=lambda item: item[1], reverse=True))
log(f"Area matches: {area_counter}")
log(f"labels: {labels}")
Expand All @@ -182,7 +315,11 @@
for area in area_counter:
collab += maintainer_file.areas[area.name].maintainers
collab += maintainer_file.areas[area.name].collaborators
collab += collab_per_path

collab = list(dict.fromkeys(collab))
# add more reviewers based on maintainer file changes.
collab += list(additional_reviews)
log(f"collab: {collab}")

_all_maintainers = dict(
Expand Down
2 changes: 1 addition & 1 deletion scripts/ci/twister_ignore.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ scripts/checkpatch.pl
scripts/ci/pylintrc
scripts/footprint/*
scripts/make_bugs_pickle.py
scripts/set_assignees.py
scripts/ci/set_assignees.py
scripts/gitlint/zephyr_commit_rules.py
scripts/west_commands/runners/canopen_program.py
scripts/ci/check_maintainer_changes.py
Expand Down
Loading
Loading