update_label_access.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. """Updates the contributors-with-label-access team.
  2. This team exists because we need a team to manage triage access to repos;
  3. GitHub doesn't allow the org to be set to triage access, only read/write. It
  4. will be updated to include all members of the carbon-language organization.
  5. """
  6. __copyright__ = """
  7. Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  8. Exceptions. See /LICENSE for license information.
  9. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  10. """
  11. import argparse
  12. # https://github.com/PyGithub/PyGithub
  13. # GraphQL is preferred, but falling back to pygithub for unsupported mutations.
  14. import github
  15. from carbon.github_tools import github_helpers
  16. # The organization to mirror members from.
  17. _ORG = "carbon-language"
  18. # The team to mirror to.
  19. _TEAM = "contributors"
  20. # Accounts in the org to skip mirroring.
  21. _IGNORE_ACCOUNTS = ("CarbonLangInfra", "google-admin", "googlebot")
  22. # Queries organization members.
  23. _ORG_MEMBER_QUERY = """
  24. query {
  25. organization(login: "%s") {
  26. membersWithRole(first: 100%%(cursor)s) {
  27. nodes {
  28. login
  29. }
  30. %%(pagination)s
  31. }
  32. }
  33. }
  34. """
  35. # The path for nodes in _ORG_MEMBER_QUERY.
  36. _ORG_MEMBER_PATH = ("organization", "membersWithRole")
  37. # Queries team members.
  38. _TEAM_MEMBER_QUERY = """
  39. query {
  40. organization(login: "%s") {
  41. team(slug: "%s") {
  42. members(first: 100%%(cursor)s) {
  43. nodes {
  44. login
  45. }
  46. %%(pagination)s
  47. }
  48. }
  49. }
  50. }
  51. """
  52. # The path for nodes in _TEAM_MEMBER_QUERY.
  53. _TEAM_MEMBER_PATH = ("organization", "team", "members")
  54. def _parse_args(args=None):
  55. """Parses command-line arguments and flags."""
  56. parser = argparse.ArgumentParser(description=__doc__)
  57. github_helpers.add_access_token_arg(parser, "admin:org, repo")
  58. return parser.parse_args(args=args)
  59. def _load_org_members(client):
  60. """Loads org members."""
  61. print("Loading %s..." % _ORG)
  62. org_members = set()
  63. ignored = set()
  64. for node in client.execute_and_paginate(
  65. _ORG_MEMBER_QUERY % _ORG, _ORG_MEMBER_PATH
  66. ):
  67. login = node["login"]
  68. if login not in _IGNORE_ACCOUNTS:
  69. org_members.add(login)
  70. else:
  71. ignored.add(login)
  72. print(
  73. "%s has %d non-ignored members, and %d ignored."
  74. % (_ORG, len(org_members), len(ignored))
  75. )
  76. unignored = set(_IGNORE_ACCOUNTS) - ignored
  77. assert not unignored, "Missing ignored accounts: %s" % unignored
  78. return org_members
  79. def _load_team_members(client):
  80. """Load team members."""
  81. print("Loading %s..." % _TEAM)
  82. team_members = set()
  83. for node in client.execute_and_paginate(
  84. _TEAM_MEMBER_QUERY % (_ORG, _TEAM), _TEAM_MEMBER_PATH
  85. ):
  86. team_members.add(node["login"])
  87. print("%s has %d members." % (_ORG, len(team_members)))
  88. return team_members
  89. def _update_team(gh, org_members, team_members):
  90. """Updates the team if needed.
  91. This switches to pygithub because GraphQL lacks equivalent mutation support.
  92. """
  93. gh_team = gh.get_organization(_ORG).get_team_by_slug(_TEAM)
  94. add_members = org_members - team_members
  95. if add_members:
  96. print("Adding members: %s" % ", ".join(add_members))
  97. for member in add_members:
  98. gh_team.add_membership(gh.get_user(member))
  99. remove_members = team_members - org_members
  100. if remove_members:
  101. print("Removing members: %s" % ", ".join(remove_members))
  102. for member in remove_members:
  103. gh_team.remove_membership(gh.get_user(member))
  104. def main():
  105. parsed_args = _parse_args()
  106. print("Connecting...")
  107. client = github_helpers.Client(parsed_args)
  108. org_members = _load_org_members(client)
  109. team_members = _load_team_members(client)
  110. if org_members != team_members:
  111. gh = github.Github(parsed_args.access_token)
  112. _update_team(gh, org_members, team_members)
  113. print("Done!")
  114. if __name__ == "__main__":
  115. main()