Skip to content

Commit c4ec344

Browse files
committed
feat(wizard): sort dependabot-for-* repos after others
1 parent fb4f5af commit c4ec344

2 files changed

Lines changed: 91 additions & 1 deletion

File tree

cvelib/wizard.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,18 @@ def _getHighestSeverity(alerts: List[Dict[str, Any]]) -> str:
301301
return priority
302302

303303

304+
def _repoGroupSortKey(item: Tuple[str, Dict[str, Any]]) -> Tuple[int, str]:
305+
"""Sort key for grouped repo items keyed by 'org/repo'.
306+
307+
Repos whose name starts with 'dependabot-for-' sort after all others,
308+
preserving lexicographic order within each group.
309+
"""
310+
repo_key, _ = item
311+
repo_name: str = repo_key.split("/", 1)[-1]
312+
is_dependabot_for: bool = repo_name.startswith("dependabot-for-")
313+
return (1 if is_dependabot_for else 0, repo_key)
314+
315+
304316
def _groupAlertsByRepo(alerts_data: List[Dict[str, Any]]) -> Dict[str, Dict[str, Any]]:
305317
"""Group alerts by repository"""
306318
grouped: Dict[str, Dict[str, Any]] = {}
@@ -1498,7 +1510,9 @@ def _runWizard(
14981510
print(f"Found alerts for {len(grouped)} repository(ies)")
14991511

15001512
# Process each repo
1501-
for i, (repo_key, repo_data) in enumerate(sorted(grouped.items()), 1):
1513+
for i, (repo_key, repo_data) in enumerate(
1514+
sorted(grouped.items(), key=_repoGroupSortKey), 1
1515+
):
15021516
org: str = repo_data["org"]
15031517
repo: str = repo_data["repo"]
15041518

tests/test_wizard.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,82 @@ def test__groupAlertsByRepo_edge_cases(self):
13061306
self.assertEqual(len(result), 1)
13071307
self.assertIn("test-org/single-repo", result)
13081308

1309+
def test__repoGroupSortKey(self):
1310+
"""Test _repoGroupSortKey assigns the correct bucket prefix"""
1311+
# Non-dependabot-for repo: bucket 0
1312+
self.assertEqual(
1313+
cvelib.wizard._repoGroupSortKey(("org/corge", {})),
1314+
(0, "org/corge"),
1315+
)
1316+
# dependabot-for-* repo: bucket 1
1317+
self.assertEqual(
1318+
cvelib.wizard._repoGroupSortKey(("org/dependabot-for-alpha", {})),
1319+
(1, "org/dependabot-for-alpha"),
1320+
)
1321+
# 'dependabot-for-' string anywhere other than the start of the repo
1322+
# name must not match
1323+
self.assertEqual(
1324+
cvelib.wizard._repoGroupSortKey(("org/my-dependabot-for-thing", {})),
1325+
(0, "org/my-dependabot-for-thing"),
1326+
)
1327+
# Org prefix containing 'dependabot-for-' must not match (split is on
1328+
# the first '/' so the org is excluded from the check)
1329+
self.assertEqual(
1330+
cvelib.wizard._repoGroupSortKey(("dependabot-for-org/corge", {})),
1331+
(0, "dependabot-for-org/corge"),
1332+
)
1333+
# Key without an org prefix still works
1334+
self.assertEqual(
1335+
cvelib.wizard._repoGroupSortKey(("dependabot-for-alpha", {})),
1336+
(1, "dependabot-for-alpha"),
1337+
)
1338+
1339+
def test__repoGroupSortKey_sorting_order(self):
1340+
"""Test _repoGroupSortKey produces the expected end-to-end order"""
1341+
grouped = {
1342+
"org/corge": {},
1343+
"org/dapper": {},
1344+
"org/dependabot-for-alpha": {},
1345+
"org/dependabot-for-beta": {},
1346+
"org/diva": {},
1347+
"org/earnest": {},
1348+
}
1349+
ordered = [
1350+
k for k, _ in sorted(grouped.items(), key=cvelib.wizard._repoGroupSortKey)
1351+
]
1352+
self.assertEqual(
1353+
ordered,
1354+
[
1355+
"org/corge",
1356+
"org/dapper",
1357+
"org/diva",
1358+
"org/earnest",
1359+
"org/dependabot-for-alpha",
1360+
"org/dependabot-for-beta",
1361+
],
1362+
)
1363+
1364+
def test__repoGroupSortKey_sorting_order_mixed_orgs(self):
1365+
"""Test _repoGroupSortKey buckets dominate across different orgs"""
1366+
grouped = {
1367+
"acme/corge": {},
1368+
"zeta/dapper": {},
1369+
"acme/dependabot-for-alpha": {},
1370+
"zeta/dependabot-for-beta": {},
1371+
}
1372+
ordered = [
1373+
k for k, _ in sorted(grouped.items(), key=cvelib.wizard._repoGroupSortKey)
1374+
]
1375+
self.assertEqual(
1376+
ordered,
1377+
[
1378+
"acme/corge",
1379+
"zeta/dapper",
1380+
"acme/dependabot-for-alpha",
1381+
"zeta/dependabot-for-beta",
1382+
],
1383+
)
1384+
13091385
@mock.patch("subprocess.run")
13101386
def test__isGhCliAvailable_failure(self, mock_subprocess):
13111387
"""Test is_gh_cli_available when gh command fails"""

0 commit comments

Comments
 (0)