Skip to content

Instantly share code, notes, and snippets.

@Roach
Created January 26, 2022 00:57
Show Gist options
  • Save Roach/589e863c60e97bd08e1c37670b286d34 to your computer and use it in GitHub Desktop.
Save Roach/589e863c60e97bd08e1c37670b286d34 to your computer and use it in GitHub Desktop.
Fetch GitHub org members with role and verified domain email
import csv
import os
import requests
import sys
# Verify the org name variable has been passed
sys_args = sys.argv
if len(sys_args)==1:
print("Please provide the GitHub org name")
print("{} <github-org>".format(sys_args[0]))
exit()
else:
github_org = "slackhq"
# Your github admin token, store this in an environment variable
# or pass it in when you invoke the script
# GITHUB_TOKEN="<your OWNER token>" python3 fetch-verified-emails.py
github_api_token = os.getenv('GITHUB_TOKEN', '')
headers = {"Authorization": "token {}".format(github_api_token)}
# Create the members_list array and add the column headers
member_list = [["name", "username", "role", "Verified emails"]]
def run_query(query):
# Query the GraphQL API
request = requests.post('https://api.github.com/graphql', json={'query': query}, headers=headers)
if request.status_code == 200:
return request.json()
else:
raise Exception("Query failed to run by returning code of {}. {}".format(request.status_code, query))
def fetch_user_data(org_name, cursor="null"):
# Get a paginated list of GitHub org members, including membership role and verified domain emails
# The GraphQL query, defined as a multi-line string.
query_template = """
{
organization(login: "%s") {
membersWithRole(first: 100, after: %s) {
edges {
cursor
node {
login
name
organizationVerifiedDomainEmails(login:"%s")
}
role
}
}
}
rateLimit {
limit
cost
remaining
resetAt
}
}
"""
# Add org name and cursor vars to the query template and run the query
query = query_template%(org_name, cursor, org_name)
result = run_query(query)
# If the results contain errors, log out the results
if "errors" in result:
print("⚠️ QUERY RETURNED WITH ERRORS")
print(result)
# Typical success and error response:
# For errors, membersWithRole will be None
# For success, 'errors' won't be present
# {
# 'data': {
# 'organization': {
# 'membersWithRole': {
# [{
# 'cursor': 'Y3Vyc29yOnYyOpHNESc=',
# 'node': {
# 'login': 'roach',
# 'name': 'Jason Roche',
# 'organizationVerifiedDomainEmails': [
# '[email protected]',
# '[email protected]'
# ]
# },
# 'role': 'OWNER'}]
# }
# },
# 'rateLimit': {
# 'limit': 5000, 'cost': 1, 'remaining': 4992, 'resetAt': '2022-01-26T00:14:27Z'
# }
# },
# 'errors': [
# {
# 'type': 'INVALID_CURSOR_ARGUMENTS',
# 'path': ['organization', 'membersWithRole', 'edges'],
# 'locations': [{'line': 5, 'column': 11}],
# 'message': '`null` does not appear to be a valid cursor.'
# }
# ]
# }
# Print the remaining rate limit count and reset timestamp
# rate_limit = result["data"]["rateLimit"]
# print("Remaining rate limit: {}, resets at: {}".format(rate_limit["remaining"],rate_limit["resetAt"]))
# Iterate through the org members
org_members = result["data"]["organization"]["membersWithRole"]["edges"]
for member_node in org_members:
member_info = member_node["node"]
# Flatten the organizationVerifiedDomainEmails list
member_emails = ""
if member_info["organizationVerifiedDomainEmails"]:
# print("Verified domain emails: {}".format(" ".join(member_info["organizationVerifiedDomainEmails"])))
member_emails = ", ".join(member_info["organizationVerifiedDomainEmails"])
member_list.append([member_info["login"], member_info["name"], member_node["role"], member_emails])
if org_members and "cursor" in org_members[-1]:
current_cursor = '"{}"'.format(org_members[-1]["cursor"])
# print("LOADING MORE MEMBERS")
fetch_user_data(org_name, current_cursor)
# Fetch the users and export to CSV
fetch_user_data(github_org)
# Verify that the member list contains more than just the header row and export to CSV
if len(member_list) > 1:
print("Found {} org members".format(len(member_list)-1))
# Export to CSV with the org name in the filename, ex: github_org_members_slackhq.csv
with open("github_org_members_{}.csv".format(github_org),"w+") as my_csv:
csvWriter = csv.writer(my_csv,delimiter=',')
csvWriter.writerows(member_list)
print("Exported CSV")
else:
print("Member list is empty.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment