From 4465ec88c9583dcb43369167a9503c9abb4d8895 Mon Sep 17 00:00:00 2001 From: Simon Schrottner Date: Thu, 28 Aug 2025 08:46:34 +0200 Subject: [PATCH] feat: add triagers to the generation of teams Signed-off-by: Simon Schrottner --- .gitignore | 1 + config/peribolos.yaml | 127 +++++++++++++++++++++++++++++ config/test-org/cli/workgroup.yaml | 2 + config/test-org/org.yaml | 69 ++++++++++++++++ config/test-org/org/workgroup.yaml | 15 ++++ go.sum | 76 ----------------- main.go | 89 +++++++++++++------- 7 files changed, 272 insertions(+), 107 deletions(-) create mode 100644 config/peribolos.yaml create mode 100644 config/test-org/cli/workgroup.yaml create mode 100644 config/test-org/org.yaml create mode 100644 config/test-org/org/workgroup.yaml diff --git a/.gitignore b/.gitignore index e2d9e9f8..351a6d85 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ dist .idea +community-tools \ No newline at end of file diff --git a/config/peribolos.yaml b/config/peribolos.yaml new file mode 100644 index 00000000..9468aac5 --- /dev/null +++ b/config/peribolos.yaml @@ -0,0 +1,127 @@ +orgs: + test-org: + admins: + - owner1 + - owner2 + default_repository_permission: read + description: Standardizing Feature Flagging for Everyone + has_organization_projects: true + has_repository_projects: true + members: + - triager1 + - approver1 + - maintainer1 + - admin1 + - globaladmin1 + - globalmaintainer1 + - globalapprover1 + - globaltriager1 + - governor1 + - tc1 + members_can_create_repositories: false + name: OpenFeature + repos: + cli: + allow_merge_commit: false + allow_rebase_merge: false + allow_squash_merge: true + description: OpenFeature’s official command-line tool + has_projects: true + has_wiki: false + org-repo1: + allow_merge_commit: false + allow_rebase_merge: false + allow_squash_merge: true + has_projects: true + has_wiki: false + org-repo2: + allow_merge_commit: false + allow_rebase_merge: false + allow_squash_merge: true + has_projects: true + has_wiki: false + teams: + Governance Board: + members: + - governor1 + repos: + cli: admin + org-repo1: admin + org-repo2: admin + Technical Steering Committee: + members: + - tc1 + repos: + cli: admin + org-repo1: admin + org-repo2: admin + admins: + members: + - globaladmin1 + repos: + cli: admin + org-repo1: admin + org-repo2: admin + approvers: + members: + - globalapprover1 + repos: + cli: write + org-repo1: write + org-repo2: write + cli-triagers: + repos: + cli: triage + teams: + cli-approvers: + repos: + cli: write + teams: + cli-maintainers: + repos: + cli: maintain + teams: + cli-admins: + repos: + cli: admin + maintainers: + members: + - globalmaintainer1 + repos: + cli: maintain + org-repo1: maintain + org-repo2: maintain + org-triagers: + members: + - triagers1 + repos: + org-repo1: triage + org-repo2: triage + teams: + org-approvers: + members: + - approver1 + repos: + org-repo1: write + org-repo2: write + teams: + org-maintainers: + members: + - maintainer1 + repos: + org-repo1: maintain + org-repo2: maintain + teams: + org-admins: + members: + - admin1 + repos: + org-repo1: admin + org-repo2: admin + triagers: + members: + - globaltriager1 + repos: + cli: triage + org-repo1: triage + org-repo2: triage diff --git a/config/test-org/cli/workgroup.yaml b/config/test-org/cli/workgroup.yaml new file mode 100644 index 00000000..8431f9b5 --- /dev/null +++ b/config/test-org/cli/workgroup.yaml @@ -0,0 +1,2 @@ +repos: + - cli diff --git a/config/test-org/org.yaml b/config/test-org/org.yaml new file mode 100644 index 00000000..13dc70b2 --- /dev/null +++ b/config/test-org/org.yaml @@ -0,0 +1,69 @@ +# org settings +name: TEST-ORG +description: testdata for testing the script + +default_repository_permission: read + +has_organization_projects: true +has_repository_projects: true +members_can_create_repositories: false + +admins: + - owner1 + - owner2 + +members: + - triager1 + - approver1 + - maintainer1 + - admin1 + + - globaladmin1 + - globalmaintainer1 + - globalapprover1 + - globaltriager1 + + - governor1 + - tc1 + +teams: + admins: + members: + - globaladmin1 + + maintainers: + members: + - globalmaintainer1 + + approvers: + members: + - globalapprover1 + triagers: + members: + - globaltriager1 + + Technical Steering Committee: + members: + - tc1 + + Governance Board: + members: + - governor1 + +repos: + # Template for adding a new project + # mandatory: + # + # : + # description: + # + # optional: + # archived: - defaults to false + # private: - defaults to false + # has_issues: - defaults to true + # has_projects: - defaults to true + # has_wiki: - defaults to false + cli: + description: OpenFeature’s official command-line tool + org-repo1: + org-repo2: diff --git a/config/test-org/org/workgroup.yaml b/config/test-org/org/workgroup.yaml new file mode 100644 index 00000000..5a970532 --- /dev/null +++ b/config/test-org/org/workgroup.yaml @@ -0,0 +1,15 @@ +repos: + - org-repo1 + - org-repo2 + +triagers: + - triagers1 + +approvers: + - approver1 + +maintainers: + - maintainer1 + +admins: + - admin1 diff --git a/go.sum b/go.sum index 2f4696bb..63cf7210 100644 --- a/go.sum +++ b/go.sum @@ -260,66 +260,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= -golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/exp v0.0.0-20240822175202-778ce7bba035 h1:VkSUcpKXdGwUpn/JsiWXwSNnIJVXRfMA4ThL5vwljWg= -golang.org/x/exp v0.0.0-20240822175202-778ce7bba035/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= -golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= -golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= -golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 h1:1wqE9dj9NpSm04INVsJhhEUzhuDVjbcyKH91sVyPATw= -golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= -golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= -golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= -golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0= -golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= -golang.org/x/exp v0.0.0-20241210172134-14434422244c h1:G0f8LmhCW7rzpybldgSjhhKDCwW7mYO0Qr6HZDb0HJA= -golang.org/x/exp v0.0.0-20241210172134-14434422244c/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= -golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4= -golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= -golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= -golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= -golang.org/x/exp v0.0.0-20250103163809-dd03c70a0a45 h1:tHbTCyM7JD+jm5mShcnp+cl1Q6nJpFZe7kyALJCclHA= -golang.org/x/exp v0.0.0-20250103163809-dd03c70a0a45/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= -golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588= -golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= -golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= -golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= -golang.org/x/exp v0.0.0-20250128144449-3edf0e91c1ae h1:COZdc9Ut6wLq7MO9GIYxfZl4n4ScmgqQLoHocKXrxco= -golang.org/x/exp v0.0.0-20250128144449-3edf0e91c1ae/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= -golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34= -golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= -golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= -golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= -golang.org/x/exp v0.0.0-20250215185904-eff6e970281f h1:oFMYAjX0867ZD2jcNiLBrI9BdpmEkvPyi5YrBGXbamg= -golang.org/x/exp v0.0.0-20250215185904-eff6e970281f/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= -golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4= -golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= -golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 h1:aWwlzYV971S4BXRS9AmqwDLAD85ouC6X+pocatKY58c= -golang.org/x/exp v0.0.0-20250228200357-dead58393ab7/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= -golang.org/x/exp v0.0.0-20250530174510-65e920069ea6 h1:gllJVKwONftmCc4KlNbN8o/LvmbxotqQy6zzi6yDQOQ= -golang.org/x/exp v0.0.0-20250530174510-65e920069ea6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= -golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA= -golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= -golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4= -golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -395,22 +335,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/main.go b/main.go index 68dcd121..2f50d525 100644 --- a/main.go +++ b/main.go @@ -26,8 +26,9 @@ const ( Admins string = "admins" Maintainers string = "maintainers" Approvers string = "approvers" - TSC string = "Technical Steering Committee" - GB string = "Governance Board" + Triagers string = "triagers" + TSC string = "Technical Steering Committee" + GB string = "Governance Board" ) type options struct { @@ -40,6 +41,7 @@ type Group struct { Admins []string `json:"admins,omitempty"` Maintainers []string `json:"maintainers,omitempty"` Approvers []string `json:"approvers,omitempty"` + Triagers []string `json:"triagers,omitempty"` } func main() { @@ -136,6 +138,7 @@ func loadOrgs(o options) (map[string]org.Config, error) { admins := getGlobalTeam(cfg, Admins) maintainers := getGlobalTeam(cfg, Maintainers) approvers := getGlobalTeam(cfg, Approvers) + triagers := getGlobalTeam(cfg, Triagers) tsc := getGlobalTeam(cfg, TSC) gb := getGlobalTeam(cfg, GB) @@ -143,6 +146,7 @@ func loadOrgs(o options) (map[string]org.Config, error) { admins.Repos[name] = github.Admin maintainers.Repos[name] = github.Maintain approvers.Repos[name] = github.Write + triagers.Repos[name] = github.Triage tsc.Repos[name] = github.Admin gb.Repos[name] = github.Admin cfg.Repos[name] = applyRepoDefaults(cfg, name) @@ -153,6 +157,7 @@ func loadOrgs(o options) (map[string]org.Config, error) { cfg.Teams[Admins] = admins cfg.Teams[Maintainers] = maintainers cfg.Teams[Approvers] = approvers + cfg.Teams[Triagers] = triagers cfg.Teams[TSC] = tsc cfg.Teams[GB] = gb config[name] = *cfg @@ -215,39 +220,61 @@ func getGlobalTeam(cfg *org.Config, teamName string) org.Team { } func generateGroupConfig(path string) (map[string]org.Team, error) { + // Unmarshal the group configuration groupCfg, err := unmarshalGroup(path) if err != nil { return nil, fmt.Errorf("error in %s: %v", path, err) } group := filepath.Base(filepath.Dir(path)) - admins := org.Team{ - Members: groupCfg.Admins, - Repos: map[string]github.RepoPermissionLevel{}, - } - maintainers := org.Team{ - Members: groupCfg.Maintainers, - Repos: map[string]github.RepoPermissionLevel{}, - Children: map[string]org.Team{ - group + "-" + Admins: admins, - }, - } - approvers := org.Team{ - Members: groupCfg.Approvers, - Repos: map[string]github.RepoPermissionLevel{}, - Children: map[string]org.Team{ - group + "-" + Maintainers: maintainers, - }, - } - - // adding repos to the all repos list - for _, repo := range groupCfg.Repos { - admins.Repos[repo] = github.Admin - maintainers.Repos[repo] = github.Maintain - approvers.Repos[repo] = github.Write - } - - teams := map[string]org.Team{} - teams[group+"-"+Approvers] = approvers - return teams, nil + + // Define the hierarchy of teams + type TeamHierarchy struct { + Name string + Role github.RepoPermissionLevel + Members []string + Children []string + } + + hierarchy := []TeamHierarchy{ + {Name: Admins, Role: github.Admin, Members: groupCfg.Admins}, + {Name: Maintainers, Role: github.Maintain, Members: groupCfg.Maintainers, Children: []string{Admins}}, + {Name: Approvers, Role: github.Write, Members: groupCfg.Approvers, Children: []string{Maintainers}}, + {Name: Triagers, Role: github.Triage, Members: groupCfg.Triagers, Children: []string{Approvers}}, + } + + // Helper function to assign repository permissions + assignRepoPermissions := func(repos []string, permission github.RepoPermissionLevel) map[string]github.RepoPermissionLevel { + repoMap := make(map[string]github.RepoPermissionLevel) + for _, repo := range repos { + repoMap[repo] = permission + } + return repoMap + } + + // Map to store all teams + teams := make(map[string]org.Team) + + // Build teams based on the hierarchy + for _, entry := range hierarchy { + children := make(map[string]org.Team) + for _, childName := range entry.Children { + childKey := group + "-" + childName + if childTeam, exists := teams[childKey]; exists { + children[childKey] = childTeam + } + } + + teamKey := group + "-" + entry.Name + teams[teamKey] = org.Team{ + Members: entry.Members, + Repos: assignRepoPermissions(groupCfg.Repos, entry.Role), + Children: children, + } + } + + // Return the top-level team (Triagers) as the root of the hierarchy + return map[string]org.Team{ + group + "-" + Triagers: teams[group+"-"+Triagers], + }, nil }