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
2 changes: 1 addition & 1 deletion orgs/org_management/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
generator = OrgGenerator()
generator.load_from_project()
if not generator.validate_repo_ownership():
print("ERROR: Repository ownership is invalid. Refer to RFC-0007.")
print("ERROR: Repository ownership is invalid. Refer to RFC-0007 and RFC-0036.")
exit(1)
generator.generate_org_members()
generator.generate_teams()
Expand Down
19 changes: 17 additions & 2 deletions orgs/org_management/org_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,20 +120,35 @@ def load_from_project(self):
raise ValueError(f"Invalid org {org} in WG {wg['name']}, expected one of {OrgGenerator._MANAGED_ORGS}")
self.working_groups[org].append(wg)

# rfc-0007-repository-ownership: a repo can't be owned by multiple WGs, scope is github org
def validate_repo_ownership(self) -> bool:
valid = True

# rfc-0007-repository-ownership: a repo can't be owned by multiple WGs, scope is github org
for org in OrgGenerator._MANAGED_ORGS:
repo_owners = {}
for wg in self.working_groups[org]:
wg_name = wg["name"]
wg_repos = set(r for a in wg["areas"] for r in a["repositories"])
for repo in wg_repos:
if repo in repo_owners:
print(f"ERROR: Repository {repo} is owned by multiple WGs: {repo_owners[repo]}, {wg_name}")
print(f"ERROR: Repository '{repo}' is owned by multiple WGs: {repo_owners[repo]}, {wg_name}")
valid = False
else:
repo_owners[repo] = wg_name

# rfc-0036-multiple-github-orgs: Working Groups MUST only contain repos from one CFF Github Org (but repos from unmanaged orgs are allowed as temporary exception)
for org in self.working_groups.keys():
for wg in self.working_groups[org]:
wg_name = wg["name"]
wg_repos = set(r for a in wg["areas"] for r in a["repositories"])
for repo in wg_repos:
repo_org = repo.split("/")[0]
if repo_org != org and repo_org in OrgGenerator._MANAGED_ORGS:
print(
f"ERROR: Working Group '{wg_name}' assigned to Github org '{org}' contains repository '{repo}' from a different managed org."
)
valid = False

return valid

def get_contributors(self, org: str) -> set[str]:
Expand Down
18 changes: 15 additions & 3 deletions orgs/org_management/test_org_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@
repositories:
- cloudfoundry2/repo3
- cloudfoundry2/repo4
- cloudfoundry/repo5
- cloudfoundry2/repo5
"""

toc = """
Expand Down Expand Up @@ -474,6 +474,18 @@ def test_validate_repo_ownership(self):
o = OrgGenerator(static_org_cfg=org_cfg, toc=toc, working_groups=[wg1, wg2, wg3])
self.assertFalse(o.validate_repo_ownership())

def test_validate_repo_ownership_multiple_orgs(self):
OrgGenerator._MANAGED_ORGS = ["cloudfoundry", "cloudfoundry2"]
o = OrgGenerator(static_org_cfg=org_cfg_multiple, toc=toc, working_groups=[wg1, wg4_other_org])
self.assertTrue(o.validate_repo_ownership())
# includes non-managed orgs
o = OrgGenerator(static_org_cfg=org_cfg_multiple, toc=toc, working_groups=[wg1, wg2, wg4_other_org])
self.assertTrue(o.validate_repo_ownership())
# wg can only have repos of one org
bad_wg4_other_org = wg4_other_org.replace("cloudfoundry2/repo5", "cloudfoundry/repo5")
o = OrgGenerator(static_org_cfg=org_cfg_multiple, toc=toc, working_groups=[wg1, bad_wg4_other_org])
self.assertFalse(o.validate_repo_ownership())

def test_generate_wg_teams(self):
_wg1 = OrgGenerator._yaml_load(wg1)
OrgGenerator._validate_wg(_wg1)
Expand Down Expand Up @@ -559,7 +571,7 @@ def test_generate_wg_teams_multiple_orgs(self):
self.assertDictEqual({"repo1": "write", "repo2": "write"}, team["repos"])

team = wg_team["teams"]["wg-wg4-name-area-2-approvers"]
self.assertDictEqual({"repo3": "write", "repo4": "write"}, team["repos"])
self.assertDictEqual({"repo3": "write", "repo4": "write", "repo5": "write"}, team["repos"])

def test_generate_toc_team(self):
_toc = OrgGenerator._yaml_load(toc)
Expand Down Expand Up @@ -758,7 +770,7 @@ def test_generate_branch_protection_multiple_orgs(self):

bp_repos = o.branch_protection["branch-protection"]["orgs"]["cloudfoundry2"]["repos"]
# wg4 opted in, repo5 is ignored because of wrong org
self.assertSetEqual({f"repo{i}" for i in range(1, 5)}, set(bp_repos.keys()))
self.assertSetEqual({f"repo{i}" for i in range(1, 6)}, set(bp_repos.keys()))
# repo1 has static config that wins over generated branch protection rules
self.assertTrue(bp_repos["repo1"]["protect"])
self.assertNotIn("required_pull_request_reviews", bp_repos["repo1"])
Expand Down
2 changes: 1 addition & 1 deletion orgs/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
description = "Automation for GitHub orgs managed by the Cloud Foundry Foundation"
readme = "readme.md"
requires-python = ">=3.14"
license-files = ["LICENSE"]
license = "Apache-2.0"
classifiers = ["Private :: Do Not Upload"]
dependencies = [
"pyyaml",
Expand Down