From 58f400353f89118012a04f5f585d63d04b680209 Mon Sep 17 00:00:00 2001 From: Vanessasaurus <814322+vsoch@users.noreply.github.com> Date: Thu, 23 Jun 2022 12:49:44 -0600 Subject: [PATCH] Add/users from orgs and event selection (#8) * allow new users * jsonify Signed-off-by: vsoch --- README.md | 29 +++++++++++++++++- action.yml | 6 ++++ scripts/entrypoint.sh | 12 +++++--- scripts/generate-events.py | 33 ++++++++++++-------- scripts/update-users.py | 62 ++++++++++++++++++++++++++++---------- 5 files changed, 109 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 0b3f298..b678fc2 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,11 @@ define an `orgs_file` which defaults to orgs.txt. The format is the same as the `users_file`. If you don't define this variable, parsing organization events will be skipped. +### `users_from_orgs_file` + +If you want to use an organization to look up public members, set this varaible. +It will default to `user-orgs.txt` and be used if it's found. + ### `query` **Optional** If defined, we will use [update-users.py](scripts/update-users.py) @@ -108,6 +113,26 @@ username2 usernameN ``` +### `events` + +A comma separated list of allowed events. By default, we use "all" to allow all events. +Events include: + +- PushEvent +- IssueCommentEvent +- PullRequestEvent +- CreateEvent +- IssueCommentEvent +- ReleaseEvent +- IssuesEvent +- PublicEvent +- PullRequestReviewCommentEvent +- PullRequestReviewEvent + +For example, maybe you are just interested in questions being asked by members of your +orgs (or repos they need help or contribution to) - you would select the IssuesEvent. +Or if you want to see contribution, you might choose the PullRequestEvent. It's up to you! + ### `token` Your GitHub token is required to define in the environment to increase query @@ -215,8 +240,10 @@ beyond the _events folder. If you use the action and want to share, please open a pull request to add your repository here! - - [Stanford Opensource Heartbeat](https://stanford-rc.github.io/opensource-stanford/) - [vsoch Opensource Heartbeat](https://vsoch.github.io/opensource-heartbeat/) + - [rse-ops Opensource Heartbeat](https://rse-ops.github.io/opensource-heartbeat/) + - [Stanford Opensource Heartbeat](https://stanford-rc.github.io/opensource-stanford/) (removed/404 when @vsoch left Stanford) + - **Coming soon** LLNL RADIUSS ## Future Wants diff --git a/action.yml b/action.yml index 7658ecd..c83ec56 100644 --- a/action.yml +++ b/action.yml @@ -9,6 +9,9 @@ inputs: description: 'This is the relative path in the repository for the users.txt (or other named file) that includes GitHub user names for users to include for parsin GitHub events.' required: true default: 'users.txt' + users_from_orgs_file: + description: "A list of organizations to find users from." + default: "user-orgs.txt" orgs_file: description: "A list of organizations to include contributions for, not updated automatically." default: "orgs.txt" @@ -22,6 +25,9 @@ inputs: query: description: 'A user query to run before updating the users_file. If left blank, will not be updated.' required: false + events: + description: 'Comma separated list of event names to parse, defaults to parsing all events' + default: all collection: description: 'The collections folder under docs to write events to. Defaults to _events.' default: _events diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh index 72d0240..36ae5e9 100755 --- a/scripts/entrypoint.sh +++ b/scripts/entrypoint.sh @@ -9,9 +9,13 @@ if [ -z "${INPUT_COLLECTION}" ]; then fi # If a query is defined, update the users file -if [ ! -z "${INPUT_QUERY}" ]; then - printf "Updating users using query: ${INPUT_QUERY}\n" - python3 /update-users.py --user-query "${INPUT_QUERY}"; +if [ ! -z "${INPUT_QUERY}" ] || [ ! -z "${INPUT_USERS_FROM_ORGS_FILE}" ]; then + cmd="python3 /update-users.py" + if [ ! -z "${INPUT_QUERY}" ]; then + printf "Updating users using query: ${INPUT_QUERY}\n" + cmd="$cmd --user-query ${INPUT_QUERY}" + fi + ${cmd} fi # If the docs folder isn't created, do so. @@ -27,5 +31,5 @@ if [ ! -z "${INPUT_CLEANUP}" ]; then fi export INPUT_COLLECTION -# Generate Stanford Issues +# Generate Issues python3 /generate-events.py diff --git a/scripts/generate-events.py b/scripts/generate-events.py index c113d22..0838dc3 100755 --- a/scripts/generate-events.py +++ b/scripts/generate-events.py @@ -2,7 +2,7 @@ """ -Copyright (C) 2020 Vanessa Sochat. +Copyright (C) 2020-2022 Vanessa Sochat. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed @@ -24,6 +24,9 @@ if token: headers = {"Authorization": f"token {token}"} +ALLOWED_EVENTS = os.environ.get("INPUT_EVENTS").split(",") or ["all"] +print(f"ALLOWED EVENTS: {ALLOWED_EVENTS}") + def get_parser(): parser = argparse.ArgumentParser(description="Open Source Heartbeat") @@ -128,6 +131,9 @@ def generate_content(event, user, seen): event_type = event["type"] avatar = event["actor"]["avatar_url"] + if "all" not in ALLOWED_EVENTS and event_type not in ALLOWED_EVENTS: + return + if user not in seen: seen[user] = {"PushEvent": [], "IssueCommentEvent": []} @@ -194,18 +200,21 @@ def generate_content(event, user, seen): issue_number = issue_url.split("/")[-1] issue_state = event["payload"]["issue"]["state"] issue_title = event["payload"]["issue"]["title"] - issue_body = event["payload"]["issue"].get('body', '') or "" + issue_body = event["payload"]["issue"].get("body", "") or "" issue_body = issue_body.split("\n")[0] + "..." - description = "%s %s issue %s#%s.\n\n

%s

%sView Comment" % ( - user, - user, - issue_state, - issue_url, - repo_name, - issue_number, - issue_title, - issue_body, - issue_url, + description = ( + "%s %s issue %s#%s.\n\n

%s

%sView Comment" + % ( + user, + user, + issue_state, + issue_url, + repo_name, + issue_number, + issue_title, + issue_body, + issue_url, + ) ) url = issue_url diff --git a/scripts/update-users.py b/scripts/update-users.py index 816d5fe..5729759 100755 --- a/scripts/update-users.py +++ b/scripts/update-users.py @@ -2,7 +2,7 @@ """ -Copyright (C) 2020 Vanessa Sochat. +Copyright (C) 2020-2022 Vanessa Sochat. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed @@ -52,6 +52,13 @@ def get_parser(): default=None, ) + parser.add_argument( + "--org-users-file", + dest="org_users_file", + help="A filename with organization names to get public users from", + default=None, + ) + return parser @@ -61,14 +68,31 @@ def get_headers(): """ # Add token to increase API limits token = os.environ.get("GITHUB_TOKEN") - headers = {} + headers = {"Accept": "application/vnd.github.v3+json"} if token: headers = {"Authorization": f"token {token}"} return headers +def get_org_users(orgs): + """ + Given a list of orgs, retrieve public users + """ + users = [] + for org in orgs: + org = org.strip() + if not org: + continue + res = requests.get(f"https://api.github.com/orgs/{org}/members") + if res.status_code != 200: + sys.exit(f"{response.status_code}: {response.reason}") + users += [x["login"] for x in res.json()] + return users + + def search_users(query, search_type="users"): - """Return a subset of users that match a particular query (up to 1000) + """ + Return a subset of users that match a particular query (up to 1000) The example here searches for location Stanford """ @@ -103,38 +127,44 @@ def main(): SKIP_USERS_FILE = args.exclude_users_file or os.environ.get( "INPUT_EXCLUDE_USERS_FILE", "exclude-users.txt" ) + USERS_FROM_ORGS_FILE = args.org_users_file or os.environ.get( + "INPUT_USERS_FROM_ORGS_FILE", "org-users.txt" + ) # Debugging for the user print(f"users file: {USERS_FILE}") print(f"skips file: {SKIP_USERS_FILE}") + print(f"users from orgs file {USERS_FROM_ORGS_FILE}") # Update users to file (so they can remove later) SEARCH_QUERY = os.environ.get("INPUT_QUERY", args.user_query) - if not SEARCH_QUERY: - sys.exit( - "You must define a --user-query to filter users. See https://github.com/search/advanced for help." - ) - users = [] if os.path.exists(USERS_FILE): users = [u for u in read_file(USERS_FILE).split("\n") if u] + # Get public users for an org + if os.path.exists(USERS_FROM_ORGS_FILE): + users += get_org_users(read_file(USERS_FROM_ORGS_FILE).split("\n")) + + # Unique + users = list(set(users)) + # If skip users is defined skip_users = [] if os.path.exists(SKIP_USERS_FILE): skip_users = [u for u in read_file(USERS_FILE).split("\n") if u] - # Update users - new_users = [ - user - for user in search_users(SEARCH_QUERY) - if user not in users and user not in skip_users - ] + # To we want to further filter? + new_users = [] + if SEARCH_QUERY: + new_users = search_users(SEARCH_QUERY) - print(f"Found {len(new_users)} new users!") - users += new_users + users = users + new_users + # Update users + users = [user for user in users if user not in skip_users] + print(f"Found {len(new_users)} users!") write_file("\n".join(users), USERS_FILE)