From db318e95de0c1bf0bb3c30798b9ac438eb5a7375 Mon Sep 17 00:00:00 2001 From: Philip Petrakian Date: Thu, 18 Jun 2026 21:31:32 +0000 Subject: [PATCH 1/7] Use fixed oncall schedule order Signed-off-by: Philip Petrakian --- .github/oncall_schedule.json | 24 ++------ .github/scripts/oncall_manager.py | 51 +++++++++------- .../python_scripts/test_oncall_manager.py | 60 +++++++++++++++++++ 3 files changed, 94 insertions(+), 41 deletions(-) diff --git a/.github/oncall_schedule.json b/.github/oncall_schedule.json index eea6acdef57..b6e2c1ff9b8 100644 --- a/.github/oncall_schedule.json +++ b/.github/oncall_schedule.json @@ -12,39 +12,27 @@ "date": "2026-07-01" }, { - "user": "wujingyue", + "user": "dimapihtar", "date": "2026-07-08" }, { - "user": "Connor-XY", + "user": "janEbert", "date": "2026-07-15" }, { - "user": "Phlip79", + "user": "ilml", "date": "2026-07-22" }, { - "user": "YangFei1990", + "user": "wujingyue", "date": "2026-07-29" }, { - "user": "asolergi-nv", + "user": "Connor-XY", "date": "2026-08-05" }, - { - "user": "dimapihtar", - "date": "2026-08-12" - }, { "user": "guihong-nv", - "date": "2026-08-19" - }, - { - "user": "ilml", - "date": "2026-08-26" - }, - { - "user": "janEbert", - "date": "2026-09-02" + "date": "2026-08-12" } ] diff --git a/.github/scripts/oncall_manager.py b/.github/scripts/oncall_manager.py index facd23c1ca8..e69bb6c6467 100644 --- a/.github/scripts/oncall_manager.py +++ b/.github/scripts/oncall_manager.py @@ -25,7 +25,6 @@ # Constants GITHUB_API_URL = "https://api.github.com" SCHEDULE_FILE = ".github/oncall_schedule.json" -ROTATION_TEAM_SLUG = "mcore-oncall-rotation" ACTIVE_ONCALL_TEAM_SLUG = "mcore-oncall" SLACK_USERGROUP_HANDLE = "mcore-oncall" COMMUNITY_REQUEST_LABEL = "community-request" @@ -258,6 +257,17 @@ def save_schedule(schedule): json.dump(schedule, f, indent=4) f.write('\n') # trailing newline +def get_schedule_user_order(schedule): + """Returns unique users from the schedule in file order.""" + ordered_users = [] + seen_users = set() + for entry in schedule: + user = entry.get('user') + if user and user not in seen_users: + ordered_users.append(user) + seen_users.add(user) + return ordered_users + def update_active_oncall_team(org, new_oncall): """Updates the active oncall team to contain only the new oncall user.""" # 1. Get current members of the active team @@ -289,6 +299,7 @@ def update_active_oncall_team(org, new_oncall): def rotate_schedule(repo_owner, dry_run=False): schedule = load_schedule() + rotation_order = get_schedule_user_order(schedule) print(f"Current schedule length: {len(schedule)}") # 1. Rotate (Remove past week) @@ -319,7 +330,7 @@ def rotate_schedule(repo_owner, dry_run=False): print("Schedule empty, nothing to rotate.") # 2. Replenish - ensure_schedule_filled(schedule, repo_owner) + ensure_schedule_filled(schedule, rotation_order) # 3. Update active oncall team if schedule: @@ -343,17 +354,12 @@ def get_last_wednesday(): offset = (today.weekday() - 2) % 7 return today - timedelta(days=offset) -def ensure_schedule_filled(schedule, repo_owner): +def ensure_schedule_filled(schedule, rotation_order=None): """Appends users to schedule until it reaches TARGET_WEEKS.""" - members = get_team_members(repo_owner, ROTATION_TEAM_SLUG) - if not members: - print(f"Warning: No team members found in {ROTATION_TEAM_SLUG}.") + rotation_order = rotation_order or get_schedule_user_order(schedule) + if not rotation_order: + print("Warning: No users found in schedule. Cannot fill schedule.") return - if 'svcnvidia-nemo-ci' in members: - members.remove('svcnvidia-nemo-ci') - members = list(members) - - members.sort() # Deterministic order while len(schedule) < TARGET_WEEKS: # Determine start date for the new entry @@ -361,8 +367,8 @@ def ensure_schedule_filled(schedule, repo_owner): # Start with the most recent Wednesday if list is empty next_date = get_last_wednesday() - # Start with the first member alphabetically if list is empty - next_user = members[0] + # Start with the first user in the hard-coded schedule order if list is empty + next_user = rotation_order[0] else: last_entry = schedule[-1] last_user = last_entry['user'] @@ -376,16 +382,16 @@ def ensure_schedule_filled(schedule, repo_owner): next_date = get_last_wednesday() + timedelta(days=7 * len(schedule)) try: - # Find index of last scheduled user in the team list - if last_user in members: - last_idx = members.index(last_user) - next_idx = (last_idx + 1) % len(members) - next_user = members[next_idx] + # Find index of last scheduled user in the hard-coded schedule order + if last_user in rotation_order: + last_idx = rotation_order.index(last_user) + next_idx = (last_idx + 1) % len(rotation_order) + next_user = rotation_order[next_idx] else: - # Last user not in team, just pick first member - next_user = members[0] + # Last user not in schedule order, just pick first user + next_user = rotation_order[0] except ValueError: - next_user = members[0] + next_user = rotation_order[0] new_entry = {"user": next_user, "date": next_date.strftime("%Y-%m-%d")} schedule.append(new_entry) @@ -459,7 +465,7 @@ def main(): rotate_schedule(owner, dry_run=args.dry_run) elif args.command == "fill": schedule = load_schedule() - ensure_schedule_filled(schedule, owner) + ensure_schedule_filled(schedule) save_schedule(schedule) print("Schedule filled and saved.") elif args.command == "assign": @@ -467,4 +473,3 @@ def main(): if __name__ == "__main__": main() - diff --git a/tests/test_utils/python_scripts/test_oncall_manager.py b/tests/test_utils/python_scripts/test_oncall_manager.py index a200bee74da..8a673598628 100644 --- a/tests/test_utils/python_scripts/test_oncall_manager.py +++ b/tests/test_utils/python_scripts/test_oncall_manager.py @@ -123,3 +123,63 @@ def test_assign_reviewer_requests_oncall_when_needed(oncall_manager, monkeypatch "json": {"team_reviewers": ["mcore-oncall"]}, } ] + + +def test_ensure_schedule_filled_uses_schedule_file_order(oncall_manager, monkeypatch): + schedule = [ + {"user": "charlie", "date": "2026-01-07"}, + {"user": "alice", "date": "2026-01-14"}, + {"user": "bob", "date": "2026-01-21"}, + ] + monkeypatch.setattr(oncall_manager, "TARGET_WEEKS", 6) + monkeypatch.setattr( + oncall_manager, + "get_team_members", + lambda *_args, **_kwargs: pytest.fail("team members should not determine oncall order"), + ) + + oncall_manager.ensure_schedule_filled(schedule) + + assert [entry["user"] for entry in schedule] == [ + "charlie", + "alice", + "bob", + "charlie", + "alice", + "bob", + ] + assert [entry["date"] for entry in schedule[-3:]] == [ + "2026-01-28", + "2026-02-04", + "2026-02-11", + ] + + +def test_rotate_schedule_keeps_popped_user_in_rotation_order(oncall_manager, monkeypatch): + schedule = [ + {"user": "charlie", "date": "2026-01-07"}, + {"user": "alice", "date": "2026-01-14"}, + {"user": "bob", "date": "2026-01-21"}, + ] + saved_schedule = [] + real_datetime = oncall_manager.datetime + + class FakeDateTime(real_datetime): + @classmethod + def now(cls, tz=None): + return real_datetime(2026, 1, 14, tzinfo=tz) + + monkeypatch.setattr(oncall_manager, "TARGET_WEEKS", 3) + monkeypatch.setattr(oncall_manager, "datetime", FakeDateTime) + monkeypatch.setattr( + oncall_manager, "load_schedule", lambda: [entry.copy() for entry in schedule] + ) + monkeypatch.setattr( + oncall_manager, "save_schedule", lambda new_schedule: saved_schedule.extend(new_schedule) + ) + monkeypatch.setattr(oncall_manager, "update_active_oncall_team", lambda *_args, **_kwargs: None) + + oncall_manager.rotate_schedule("NVIDIA") + + assert [entry["user"] for entry in saved_schedule] == ["alice", "bob", "charlie"] + assert saved_schedule[-1]["date"] == "2026-01-28" From 4aba43095f1471f0e8ad2cdb0e037341ecaf26f6 Mon Sep 17 00:00:00 2001 From: Philip Petrakian Date: Thu, 18 Jun 2026 21:34:36 +0000 Subject: [PATCH 2/7] Update oncall schedule Signed-off-by: Philip Petrakian --- .github/oncall_schedule.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/oncall_schedule.json b/.github/oncall_schedule.json index b6e2c1ff9b8..b633dfd31db 100644 --- a/.github/oncall_schedule.json +++ b/.github/oncall_schedule.json @@ -34,5 +34,17 @@ { "user": "guihong-nv", "date": "2026-08-12" + }, + { + "user": "Phlip79", + "date": "2026-08-19" + }, + { + "user": "asolergi-nv", + "date": "2026-08-26" + }, + { + "user": "maanug-nv", + "date": "2026-09-02" } ] From 9c431cd9ce4f75b7710ba4a1b5a99a89d94dab5e Mon Sep 17 00:00:00 2001 From: Philip Petrakian Date: Thu, 18 Jun 2026 21:40:14 +0000 Subject: [PATCH 3/7] Validate oncall schedule users Signed-off-by: Philip Petrakian --- .github/scripts/oncall_manager.py | 30 +++++++++- .../python_scripts/test_oncall_manager.py | 56 +++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/.github/scripts/oncall_manager.py b/.github/scripts/oncall_manager.py index e69bb6c6467..7dfa4b792ec 100644 --- a/.github/scripts/oncall_manager.py +++ b/.github/scripts/oncall_manager.py @@ -25,6 +25,7 @@ # Constants GITHUB_API_URL = "https://api.github.com" SCHEDULE_FILE = ".github/oncall_schedule.json" +ROTATION_TEAM_SLUG = "mcore-oncall-rotation" ACTIVE_ONCALL_TEAM_SLUG = "mcore-oncall" SLACK_USERGROUP_HANDLE = "mcore-oncall" COMMUNITY_REQUEST_LABEL = "community-request" @@ -43,6 +44,11 @@ def get_headers(): if not token: print("Error: GH_TOKEN or GITHUB_TOKEN not set") sys.exit(1) + + token = token.strip() + if not token or any(char.isspace() for char in token): + print("Error: GH_TOKEN or GITHUB_TOKEN is invalid") + sys.exit(1) return { "Authorization": f"token {token}", @@ -268,6 +274,25 @@ def get_schedule_user_order(schedule): seen_users.add(user) return ordered_users +def validate_schedule_users_in_rotation_team(schedule, repo_owner): + """Validates scheduled users are members of the rotation team.""" + schedule_users = get_schedule_user_order(schedule) + if not schedule_users: + print("Warning: No users found in schedule. Cannot validate rotation team membership.") + return schedule_users + + rotation_team_members = get_team_members(repo_owner, ROTATION_TEAM_SLUG) + missing_users = sorted(set(schedule_users) - rotation_team_members) + if missing_users: + print( + f"Error: Scheduled oncall user(s) are not members of " + f"{ROTATION_TEAM_SLUG}: {', '.join(missing_users)}" + ) + sys.exit(1) + + print(f"Validated {len(schedule_users)} scheduled user(s) in {ROTATION_TEAM_SLUG}.") + return schedule_users + def update_active_oncall_team(org, new_oncall): """Updates the active oncall team to contain only the new oncall user.""" # 1. Get current members of the active team @@ -299,7 +324,7 @@ def update_active_oncall_team(org, new_oncall): def rotate_schedule(repo_owner, dry_run=False): schedule = load_schedule() - rotation_order = get_schedule_user_order(schedule) + rotation_order = validate_schedule_users_in_rotation_team(schedule, repo_owner) print(f"Current schedule length: {len(schedule)}") # 1. Rotate (Remove past week) @@ -465,7 +490,8 @@ def main(): rotate_schedule(owner, dry_run=args.dry_run) elif args.command == "fill": schedule = load_schedule() - ensure_schedule_filled(schedule) + rotation_order = validate_schedule_users_in_rotation_team(schedule, owner) + ensure_schedule_filled(schedule, rotation_order) save_schedule(schedule) print("Schedule filled and saved.") elif args.command == "assign": diff --git a/tests/test_utils/python_scripts/test_oncall_manager.py b/tests/test_utils/python_scripts/test_oncall_manager.py index 8a673598628..21b6db259d2 100644 --- a/tests/test_utils/python_scripts/test_oncall_manager.py +++ b/tests/test_utils/python_scripts/test_oncall_manager.py @@ -125,6 +125,16 @@ def test_assign_reviewer_requests_oncall_when_needed(oncall_manager, monkeypatch ] +def test_get_headers_rejects_invalid_token(oncall_manager, monkeypatch, capsys): + monkeypatch.setenv("GH_TOKEN", "not a token\nwith newline") + + with pytest.raises(SystemExit) as error: + oncall_manager.get_headers() + + assert error.value.code == 1 + assert "GH_TOKEN or GITHUB_TOKEN is invalid" in capsys.readouterr().out + + def test_ensure_schedule_filled_uses_schedule_file_order(oncall_manager, monkeypatch): schedule = [ {"user": "charlie", "date": "2026-01-07"}, @@ -155,6 +165,47 @@ def test_ensure_schedule_filled_uses_schedule_file_order(oncall_manager, monkeyp ] +def test_validate_schedule_users_in_rotation_team_accepts_all_users( + oncall_manager, monkeypatch, capsys +): + schedule = [ + {"user": "charlie", "date": "2026-01-07"}, + {"user": "alice", "date": "2026-01-14"}, + {"user": "bob", "date": "2026-01-21"}, + {"user": "alice", "date": "2026-01-28"}, + ] + monkeypatch.setattr( + oncall_manager, + "get_team_members", + lambda org, team_slug: {"alice", "bob", "charlie", "dana"}, + ) + + rotation_order = oncall_manager.validate_schedule_users_in_rotation_team(schedule, "NVIDIA") + + assert rotation_order == ["charlie", "alice", "bob"] + assert "Validated 3 scheduled user(s) in mcore-oncall-rotation" in capsys.readouterr().out + + +def test_validate_schedule_users_in_rotation_team_rejects_missing_user( + oncall_manager, monkeypatch, capsys +): + schedule = [ + {"user": "charlie", "date": "2026-01-07"}, + {"user": "alice", "date": "2026-01-14"}, + ] + monkeypatch.setattr( + oncall_manager, + "get_team_members", + lambda org, team_slug: {"alice"}, + ) + + with pytest.raises(SystemExit) as error: + oncall_manager.validate_schedule_users_in_rotation_team(schedule, "NVIDIA") + + assert error.value.code == 1 + assert "charlie" in capsys.readouterr().out + + def test_rotate_schedule_keeps_popped_user_in_rotation_order(oncall_manager, monkeypatch): schedule = [ {"user": "charlie", "date": "2026-01-07"}, @@ -177,6 +228,11 @@ def now(cls, tz=None): monkeypatch.setattr( oncall_manager, "save_schedule", lambda new_schedule: saved_schedule.extend(new_schedule) ) + monkeypatch.setattr( + oncall_manager, + "get_team_members", + lambda org, team_slug: {"alice", "bob", "charlie"}, + ) monkeypatch.setattr(oncall_manager, "update_active_oncall_team", lambda *_args, **_kwargs: None) oncall_manager.rotate_schedule("NVIDIA") From c7284369299ab505dd0115c8e8e036c800a4b1ca Mon Sep 17 00:00:00 2001 From: Philip Petrakian Date: Thu, 18 Jun 2026 21:44:08 +0000 Subject: [PATCH 4/7] Format oncall manager tests Signed-off-by: Philip Petrakian --- .../python_scripts/test_oncall_manager.py | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/tests/test_utils/python_scripts/test_oncall_manager.py b/tests/test_utils/python_scripts/test_oncall_manager.py index 21b6db259d2..d1a975b131f 100644 --- a/tests/test_utils/python_scripts/test_oncall_manager.py +++ b/tests/test_utils/python_scripts/test_oncall_manager.py @@ -158,11 +158,7 @@ def test_ensure_schedule_filled_uses_schedule_file_order(oncall_manager, monkeyp "alice", "bob", ] - assert [entry["date"] for entry in schedule[-3:]] == [ - "2026-01-28", - "2026-02-04", - "2026-02-11", - ] + assert [entry["date"] for entry in schedule[-3:]] == ["2026-01-28", "2026-02-04", "2026-02-11"] def test_validate_schedule_users_in_rotation_team_accepts_all_users( @@ -189,15 +185,8 @@ def test_validate_schedule_users_in_rotation_team_accepts_all_users( def test_validate_schedule_users_in_rotation_team_rejects_missing_user( oncall_manager, monkeypatch, capsys ): - schedule = [ - {"user": "charlie", "date": "2026-01-07"}, - {"user": "alice", "date": "2026-01-14"}, - ] - monkeypatch.setattr( - oncall_manager, - "get_team_members", - lambda org, team_slug: {"alice"}, - ) + schedule = [{"user": "charlie", "date": "2026-01-07"}, {"user": "alice", "date": "2026-01-14"}] + monkeypatch.setattr(oncall_manager, "get_team_members", lambda org, team_slug: {"alice"}) with pytest.raises(SystemExit) as error: oncall_manager.validate_schedule_users_in_rotation_team(schedule, "NVIDIA") @@ -229,9 +218,7 @@ def now(cls, tz=None): oncall_manager, "save_schedule", lambda new_schedule: saved_schedule.extend(new_schedule) ) monkeypatch.setattr( - oncall_manager, - "get_team_members", - lambda org, team_slug: {"alice", "bob", "charlie"}, + oncall_manager, "get_team_members", lambda org, team_slug: {"alice", "bob", "charlie"} ) monkeypatch.setattr(oncall_manager, "update_active_oncall_team", lambda *_args, **_kwargs: None) From 5a820e57289e89339c435206ce63f5b404efd1f9 Mon Sep 17 00:00:00 2001 From: Philip Petrakian Date: Mon, 22 Jun 2026 18:33:14 +0000 Subject: [PATCH 5/7] Use rotation team for oncall order Signed-off-by: Philip Petrakian --- .github/oncall_schedule.json | 22 ++++----- .github/scripts/oncall_manager.py | 46 +++++++++---------- .../python_scripts/test_oncall_manager.py | 46 ++++++++++--------- 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/.github/oncall_schedule.json b/.github/oncall_schedule.json index b633dfd31db..358b2720c18 100644 --- a/.github/oncall_schedule.json +++ b/.github/oncall_schedule.json @@ -1,42 +1,42 @@ [ { - "user": "Phlip79", + "user": "asolergi-nv", "date": "2026-06-17" }, { - "user": "asolergi-nv", + "user": "Connor-XY", "date": "2026-06-24" }, { - "user": "maanug-nv", + "user": "dimapihtar", "date": "2026-07-01" }, { - "user": "dimapihtar", + "user": "guihong-nv", "date": "2026-07-08" }, { - "user": "janEbert", + "user": "ilml", "date": "2026-07-15" }, { - "user": "ilml", + "user": "janEbert", "date": "2026-07-22" }, { - "user": "wujingyue", + "user": "maanug-nv", "date": "2026-07-29" }, { - "user": "Connor-XY", + "user": "Phlip79", "date": "2026-08-05" }, { - "user": "guihong-nv", + "user": "wujingyue", "date": "2026-08-12" }, { - "user": "Phlip79", + "user": "YangFei1990", "date": "2026-08-19" }, { @@ -44,7 +44,7 @@ "date": "2026-08-26" }, { - "user": "maanug-nv", + "user": "Connor-XY", "date": "2026-09-02" } ] diff --git a/.github/scripts/oncall_manager.py b/.github/scripts/oncall_manager.py index 7dfa4b792ec..e66406fabe4 100644 --- a/.github/scripts/oncall_manager.py +++ b/.github/scripts/oncall_manager.py @@ -29,6 +29,7 @@ ACTIVE_ONCALL_TEAM_SLUG = "mcore-oncall" SLACK_USERGROUP_HANDLE = "mcore-oncall" COMMUNITY_REQUEST_LABEL = "community-request" +SERVICE_ACCOUNT_USERNAME = "svcnvidia-nemo-ci" TARGET_WEEKS = 12 # Caches for email and Slack lookups @@ -263,26 +264,25 @@ def save_schedule(schedule): json.dump(schedule, f, indent=4) f.write('\n') # trailing newline -def get_schedule_user_order(schedule): - """Returns unique users from the schedule in file order.""" - ordered_users = [] - seen_users = set() - for entry in schedule: - user = entry.get('user') - if user and user not in seen_users: - ordered_users.append(user) - seen_users.add(user) - return ordered_users - -def validate_schedule_users_in_rotation_team(schedule, repo_owner): +def get_rotation_order(repo_owner): + """Returns rotation team members in alphabetical order.""" + members = get_team_members(repo_owner, ROTATION_TEAM_SLUG) + members.discard(SERVICE_ACCOUNT_USERNAME) + return sorted(members, key=str.casefold) + +def validate_schedule_users_in_rotation_team(schedule, rotation_order): """Validates scheduled users are members of the rotation team.""" - schedule_users = get_schedule_user_order(schedule) + schedule_users = {entry.get('user') for entry in schedule if entry.get('user')} if not schedule_users: print("Warning: No users found in schedule. Cannot validate rotation team membership.") - return schedule_users + return + + rotation_team_members = set(rotation_order) + if not rotation_team_members: + print(f"Error: No members found in {ROTATION_TEAM_SLUG}.") + sys.exit(1) - rotation_team_members = get_team_members(repo_owner, ROTATION_TEAM_SLUG) - missing_users = sorted(set(schedule_users) - rotation_team_members) + missing_users = sorted(schedule_users - rotation_team_members, key=str.casefold) if missing_users: print( f"Error: Scheduled oncall user(s) are not members of " @@ -291,7 +291,6 @@ def validate_schedule_users_in_rotation_team(schedule, repo_owner): sys.exit(1) print(f"Validated {len(schedule_users)} scheduled user(s) in {ROTATION_TEAM_SLUG}.") - return schedule_users def update_active_oncall_team(org, new_oncall): """Updates the active oncall team to contain only the new oncall user.""" @@ -324,7 +323,8 @@ def update_active_oncall_team(org, new_oncall): def rotate_schedule(repo_owner, dry_run=False): schedule = load_schedule() - rotation_order = validate_schedule_users_in_rotation_team(schedule, repo_owner) + rotation_order = get_rotation_order(repo_owner) + validate_schedule_users_in_rotation_team(schedule, rotation_order) print(f"Current schedule length: {len(schedule)}") # 1. Rotate (Remove past week) @@ -381,9 +381,8 @@ def get_last_wednesday(): def ensure_schedule_filled(schedule, rotation_order=None): """Appends users to schedule until it reaches TARGET_WEEKS.""" - rotation_order = rotation_order or get_schedule_user_order(schedule) if not rotation_order: - print("Warning: No users found in schedule. Cannot fill schedule.") + print(f"Warning: No users found in {ROTATION_TEAM_SLUG}. Cannot fill schedule.") return while len(schedule) < TARGET_WEEKS: @@ -392,7 +391,7 @@ def ensure_schedule_filled(schedule, rotation_order=None): # Start with the most recent Wednesday if list is empty next_date = get_last_wednesday() - # Start with the first user in the hard-coded schedule order if list is empty + # Start with the first user in the rotation team order if list is empty next_user = rotation_order[0] else: last_entry = schedule[-1] @@ -407,7 +406,7 @@ def ensure_schedule_filled(schedule, rotation_order=None): next_date = get_last_wednesday() + timedelta(days=7 * len(schedule)) try: - # Find index of last scheduled user in the hard-coded schedule order + # Find index of last scheduled user in the rotation team order if last_user in rotation_order: last_idx = rotation_order.index(last_user) next_idx = (last_idx + 1) % len(rotation_order) @@ -490,7 +489,8 @@ def main(): rotate_schedule(owner, dry_run=args.dry_run) elif args.command == "fill": schedule = load_schedule() - rotation_order = validate_schedule_users_in_rotation_team(schedule, owner) + rotation_order = get_rotation_order(owner) + validate_schedule_users_in_rotation_team(schedule, rotation_order) ensure_schedule_filled(schedule, rotation_order) save_schedule(schedule) print("Schedule filled and saved.") diff --git a/tests/test_utils/python_scripts/test_oncall_manager.py b/tests/test_utils/python_scripts/test_oncall_manager.py index d1a975b131f..4a014a7b310 100644 --- a/tests/test_utils/python_scripts/test_oncall_manager.py +++ b/tests/test_utils/python_scripts/test_oncall_manager.py @@ -135,30 +135,35 @@ def test_get_headers_rejects_invalid_token(oncall_manager, monkeypatch, capsys): assert "GH_TOKEN or GITHUB_TOKEN is invalid" in capsys.readouterr().out -def test_ensure_schedule_filled_uses_schedule_file_order(oncall_manager, monkeypatch): - schedule = [ - {"user": "charlie", "date": "2026-01-07"}, - {"user": "alice", "date": "2026-01-14"}, - {"user": "bob", "date": "2026-01-21"}, - ] - monkeypatch.setattr(oncall_manager, "TARGET_WEEKS", 6) +def test_get_rotation_order_uses_alphabetical_rotation_team(oncall_manager, monkeypatch): + monkeypatch.setattr( + oncall_manager, + "get_team_members", + lambda org, team_slug: {"charlie", "Alice", "bob", "svcnvidia-nemo-ci"}, + ) + + assert oncall_manager.get_rotation_order("NVIDIA") == ["Alice", "bob", "charlie"] + + +def test_ensure_schedule_filled_uses_rotation_team_order(oncall_manager, monkeypatch): + schedule = [{"user": "bob", "date": "2026-01-07"}] + rotation_order = ["Alice", "bob", "charlie"] + monkeypatch.setattr(oncall_manager, "TARGET_WEEKS", 5) monkeypatch.setattr( oncall_manager, "get_team_members", lambda *_args, **_kwargs: pytest.fail("team members should not determine oncall order"), ) - oncall_manager.ensure_schedule_filled(schedule) + oncall_manager.ensure_schedule_filled(schedule, rotation_order) - assert [entry["user"] for entry in schedule] == [ - "charlie", - "alice", - "bob", - "charlie", - "alice", - "bob", + assert [entry["user"] for entry in schedule] == ["bob", "charlie", "Alice", "bob", "charlie"] + assert [entry["date"] for entry in schedule[-4:]] == [ + "2026-01-14", + "2026-01-21", + "2026-01-28", + "2026-02-04", ] - assert [entry["date"] for entry in schedule[-3:]] == ["2026-01-28", "2026-02-04", "2026-02-11"] def test_validate_schedule_users_in_rotation_team_accepts_all_users( @@ -176,9 +181,10 @@ def test_validate_schedule_users_in_rotation_team_accepts_all_users( lambda org, team_slug: {"alice", "bob", "charlie", "dana"}, ) - rotation_order = oncall_manager.validate_schedule_users_in_rotation_team(schedule, "NVIDIA") + rotation_order = ["alice", "bob", "charlie", "dana"] + + oncall_manager.validate_schedule_users_in_rotation_team(schedule, rotation_order) - assert rotation_order == ["charlie", "alice", "bob"] assert "Validated 3 scheduled user(s) in mcore-oncall-rotation" in capsys.readouterr().out @@ -186,10 +192,8 @@ def test_validate_schedule_users_in_rotation_team_rejects_missing_user( oncall_manager, monkeypatch, capsys ): schedule = [{"user": "charlie", "date": "2026-01-07"}, {"user": "alice", "date": "2026-01-14"}] - monkeypatch.setattr(oncall_manager, "get_team_members", lambda org, team_slug: {"alice"}) - with pytest.raises(SystemExit) as error: - oncall_manager.validate_schedule_users_in_rotation_team(schedule, "NVIDIA") + oncall_manager.validate_schedule_users_in_rotation_team(schedule, ["alice"]) assert error.value.code == 1 assert "charlie" in capsys.readouterr().out From 49091c10c4cec0376b71fc8bf65aff5a1b614a42 Mon Sep 17 00:00:00 2001 From: Philip Petrakian Date: Mon, 22 Jun 2026 18:34:21 +0000 Subject: [PATCH 6/7] Keep oncall schedule unchanged Signed-off-by: Philip Petrakian --- .github/oncall_schedule.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/oncall_schedule.json b/.github/oncall_schedule.json index 358b2720c18..b633dfd31db 100644 --- a/.github/oncall_schedule.json +++ b/.github/oncall_schedule.json @@ -1,42 +1,42 @@ [ { - "user": "asolergi-nv", + "user": "Phlip79", "date": "2026-06-17" }, { - "user": "Connor-XY", + "user": "asolergi-nv", "date": "2026-06-24" }, { - "user": "dimapihtar", + "user": "maanug-nv", "date": "2026-07-01" }, { - "user": "guihong-nv", + "user": "dimapihtar", "date": "2026-07-08" }, { - "user": "ilml", + "user": "janEbert", "date": "2026-07-15" }, { - "user": "janEbert", + "user": "ilml", "date": "2026-07-22" }, { - "user": "maanug-nv", + "user": "wujingyue", "date": "2026-07-29" }, { - "user": "Phlip79", + "user": "Connor-XY", "date": "2026-08-05" }, { - "user": "wujingyue", + "user": "guihong-nv", "date": "2026-08-12" }, { - "user": "YangFei1990", + "user": "Phlip79", "date": "2026-08-19" }, { @@ -44,7 +44,7 @@ "date": "2026-08-26" }, { - "user": "Connor-XY", + "user": "maanug-nv", "date": "2026-09-02" } ] From 90e72bc106f6f13352daecf4174c20a7f4c0d90e Mon Sep 17 00:00:00 2001 From: Philip Petrakian Date: Mon, 22 Jun 2026 18:37:03 +0000 Subject: [PATCH 7/7] Update oncall schedule from rotation team Signed-off-by: Philip Petrakian --- .github/oncall_schedule.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/oncall_schedule.json b/.github/oncall_schedule.json index b633dfd31db..3e758dc6276 100644 --- a/.github/oncall_schedule.json +++ b/.github/oncall_schedule.json @@ -8,7 +8,7 @@ "date": "2026-06-24" }, { - "user": "maanug-nv", + "user": "Connor-XY", "date": "2026-07-01" }, { @@ -16,7 +16,7 @@ "date": "2026-07-08" }, { - "user": "janEbert", + "user": "guihong-nv", "date": "2026-07-15" }, { @@ -24,27 +24,27 @@ "date": "2026-07-22" }, { - "user": "wujingyue", + "user": "janEbert", "date": "2026-07-29" }, { - "user": "Connor-XY", + "user": "maanug-nv", "date": "2026-08-05" }, { - "user": "guihong-nv", + "user": "Phlip79", "date": "2026-08-12" }, { - "user": "Phlip79", + "user": "wujingyue", "date": "2026-08-19" }, { - "user": "asolergi-nv", + "user": "YangFei1990", "date": "2026-08-26" }, { - "user": "maanug-nv", + "user": "asolergi-nv", "date": "2026-09-02" } ]