Changes needed in lftools for github orgs 62/16262/15
authorAric Gardner <agardner@linuxfoundation.org>
Thu, 18 Jul 2019 20:42:51 +0000 (16:42 -0400)
committerAric Gardner <agardner@linuxfoundation.org>
Tue, 13 Aug 2019 17:07:14 +0000 (13:07 -0400)
Part of the effort for self serve user management

check-votes now works against a a githubpr
Note: You can't vote on your own pr so I add the pr owner as
a voter automatically.

infofile match-ldap-to-info now works against github groups

github submit-pr will submit a pr if mergable

github votes will count votes on a pr

Fixes a bug in github team invitation.
Teams needs to be array of :class:`github.Team.Team`

adds: --add_team and --remove_team to  github update-repo
so that the replication user can be added to each new repo
in an automated way

ISSUE: RELENG-1864
Signed-off-by: Aric Gardner <agardner@linuxfoundation.org>
Change-Id: I65d30dab1d1638123fdb46bc705ec6190cb3182a

docs/commands/github.rst
docs/commands/infofile.rst
lftools/cli/github_cli.py
lftools/cli/infofile.py
lftools/cli/lfidapi.py
lftools/github_helper.py [new file with mode: 0755]
lftools/lfidapi.py
releasenotes/notes/github-1e99906af8ef75ac.yaml [new file with mode: 0644]
releasenotes/notes/infofile-4dec08c571b39df8.yaml [new file with mode: 0644]
releasenotes/notes/lfidapi-d597e5ef08fda9f7.yaml [new file with mode: 0644]

index 37bd08e..c64df6a 100644 (file)
@@ -23,12 +23,33 @@ list
 
 .. program-output:: lftools github list --help
 
+
+update-repo
+-----------
+
+.. program-output:: lftools github update-repo --help
+
+
+submit-pr
+---------
+
+.. program-output:: lftools github submit-pr --help
+
+
 user
 ----
 
 .. program-output:: lftools github user --help
 
-API requires an [github] section in ~/.config/lftools/lftools.ini:
+
+votes
+-----
+
+.. program-output:: lftools github votes --help
+
+
+
+API requires a [github] section in ~/.config/lftools/lftools.ini:
 
 .. code-block:: bash
 
index 33f02cc..227254d 100644 (file)
@@ -21,3 +21,12 @@ sync-committers
 ---------------
 
  .. program-output:: lftools infofile sync-committers --help
+
+
+API for check votes requires a [github] section in ~/.config/lftools/lftools.ini:
+
+.. code-block:: bash
+
+   [github]
+   token = REDACTED
+
index 4a2d782..53b0840 100644 (file)
@@ -18,6 +18,9 @@ from github import Github
 from github import GithubException
 
 from lftools import config
+from lftools.github_helper import helper_list
+from lftools.github_helper import helper_user_github
+from lftools.github_helper import prvotes
 
 
 @click.group()
@@ -27,6 +30,43 @@ def github_cli(ctx):
     pass
 
 
+@click.command(name='submit-pr')
+@click.argument('organization')
+@click.argument('repo')
+@click.argument('pr', type=int)
+@click.pass_context
+def submit_pr(ctx, organization, repo, pr):
+    """Submit a pr if mergeable."""
+    token = config.get_setting("github", "token")
+    g = Github(token)
+    orgName = organization
+    try:
+        org = g.get_organization(orgName)
+    except GithubException as ghe:
+        print(ghe)
+
+    repo = org.get_repo(repo)
+    pr_mergable = repo.get_pull(pr).mergeable
+
+    if pr_mergable:
+        print(pr_mergable)
+        repo.get_pull(pr).merge(commit_message="Vote Completed, merging INFO file")
+    else:
+        print("PR NOT MERGABLE {}".format(pr_mergable))
+        sys.exit(1)
+
+
+@click.command(name='votes')
+@click.argument('organization')
+@click.argument('repo')
+@click.argument('pr', type=int)
+@click.pass_context
+def votes(ctx, organization, repo, pr):
+    """Helper for votes."""
+    approval_list = prvotes(organization, repo, pr)
+    print("Approvals:", approval_list)
+
+
 @click.command(name='list')
 @click.argument('organization')
 @click.option('--audit', is_flag=True, required=False,
@@ -37,90 +77,14 @@ def github_cli(ctx):
               help='All members and their respective teams')
 @click.option('--teams', is_flag=True, required=False,
               help='List avaliable teams')
+@click.option('--team', type=str, required=False,
+              help='List members of a team')
 @click.option('--repofeatures', is_flag=True, required=False,
               help='List enabled features for repos in an org')
 @click.pass_context
-def list(ctx, organization, repos, audit, full, teams, repofeatures):
-    """List an Organization's GitHub repos."""
-    token = config.get_setting("github", "token")
-    g = Github(token)
-    orgName = organization
-
-    try:
-        org = g.get_organization(orgName)
-    except GithubException as ghe:
-        print(ghe)
-
-    if repos:
-        print("All repos for organization: ", orgName)
-        repos = org.get_repos()
-        for repo in repos:
-            print(repo.name)
-
-    if audit:
-        print("{} members without 2fa:".format(orgName))
-        try:
-            members = org.get_members(filter_="2fa_disabled")
-        except GithubException as ghe:
-            print(ghe)
-        for member in members:
-            print(member.login)
-        print("{} outside collaborators without 2fa:".format(orgName))
-        try:
-            collaborators = org.get_outside_collaborators(filter_="2fa_disabled")
-        except GithubException as ghe:
-            print(ghe)
-        for collaborator in collaborators:
-            print(collaborator.login)
-
-    if repofeatures:
-        repos = org.get_repos()
-        for repo in repos:
-            print("{} wiki:{} issues:{}".format(repo.name, repo.has_wiki, repo.has_issues))
-            issues = repo.get_issues
-            for issue in issues():
-                print("{}".format(issue))
-
-    if full:
-        print("---")
-        print("#  All owners for {}:".format(orgName))
-        print("{}-owners:".format(orgName))
-
-        try:
-            members = org.get_members(role="admin")
-        except GithubException as ghe:
-            print(ghe)
-        for member in members:
-            print("  - '{}'".format(member.login))
-        print("#  All members for {}".format(orgName))
-        print("{}-members:".format(orgName))
-
-        try:
-            members = org.get_members()
-        except GithubException as ghe:
-            print(ghe)
-        for member in members:
-            print("  - '{}'".format(member.login))
-        print("#  All members and all teams for {}".format(orgName))
-
-        try:
-            teams = org.get_teams
-        except GithubException as ghe:
-            print(ghe)
-        for team in teams():
-            print("{}:".format(team.name))
-            for user in team.get_members():
-                print("  - '{}'".format(user.login))
-            print("")
-        teams = None
-
-    if teams:
-        try:
-            teams = org.get_teams
-        except GithubException as ghe:
-            print(ghe)
-        for team in teams():
-            print("{}".format(team.name))
+def list(ctx, organization, repos, audit, full, teams, team, repofeatures):
+    """List options for github org repos."""
+    helper_list(ctx, organization, repos, audit, full, teams, team, repofeatures)
 
 
 @click.command(name='create-repo')
@@ -171,7 +135,7 @@ def createrepo(ctx, organization, repository, description, has_issues, has_proje
         print(ghe)
 
 
-@click.command(name='modify-repo')
+@click.command(name='update-repo')
 @click.argument('organization')
 @click.argument('repository')
 @click.option('--has_issues', is_flag=True, required=False,
@@ -180,9 +144,13 @@ def createrepo(ctx, organization, repository, description, has_issues, has_proje
               help='Repo should have projects')
 @click.option('--has_wiki', is_flag=True, required=False,
               help='Repo should have wiki')
+@click.option('--add_team', type=str, required=False,
+              help='Add team to repo')
+@click.option('--remove_team', type=str, required=False,
+              help='remove team from repo')
 @click.pass_context
-def modifyrepo(ctx, organization, repository, has_issues, has_projects, has_wiki):
-    """Modify a Github repo within an Organization.
+def updaterepo(ctx, organization, repository, has_issues, has_projects, has_wiki, add_team, remove_team):
+    """Update a Github repo within an Organization.
 
     By default has_issues has_wiki and has_projects is set to false.
     See --help to use this command to enable these options.
@@ -202,11 +170,28 @@ def modifyrepo(ctx, organization, repository, has_issues, has_projects, has_wiki
 
     repos = org.get_repos()
 
-    for repo in repos:
-        if repo.name == repository:
-            repo.edit(has_issues=has_issues)
-            repo.edit(has_wiki=has_wiki)
-            repo.edit(has_wiki=has_projects)
+    if has_wiki or has_issues or has_projects:
+        for repo in repos:
+            if repo.name == repository:
+                repo.edit(has_issues=has_issues)
+                repo.edit(has_wiki=has_wiki)
+                repo.edit(has_wiki=has_projects)
+
+    if add_team or remove_team:
+        teams = org.get_teams
+
+        for repo in repos:
+            if repo.name == repository:
+                repo_actual = (repo)
+
+        for team in teams():
+            if team.name == add_team:
+                print(team.id)
+                team.add_to_repos(repo_actual)
+                team.set_repo_permission(repo_actual, "write")
+            if team.name == remove_team:
+                print(team.id)
+                team.remove_from_repos(repo_actual)
 
 
 @click.command(name='create-team')
@@ -272,74 +257,13 @@ def createteam(ctx, organization, name, repo, privacy):
 @click.pass_context
 def user(ctx, organization, user, team, delete, admin):
     """Add and Remove users from an org team."""
-    token = config.get_setting("github", "token")
-    g = Github(token)
-    orgName = organization
-    try:
-        org = g.get_organization(orgName)
-    except GithubException as ghe:
-        print(ghe)
-    try:
-        user_object = g.get_user(user)
-        print(user_object)
-    except GithubException as ghe:
-        print(ghe)
-        print("user {} not found".format(user))
-        sys.exit(1)
-    # check if user is a member
-    try:
-        is_member = org.has_in_members(user_object)
-        print("Is {} a member of org {}".format(user, is_member))
-    except GithubException as ghe:
-        print(ghe)
-    # get teams
-    try:
-        teams = org.get_teams
-    except GithubException as ghe:
-        print(ghe)
-
-    my_teams = [team]
-    teams = [team for team in teams() if team.name in my_teams]
-
-    if delete:
-        if is_member:
-            for t in teams:
-                team_id = (t.id)
-            try:
-                team = org.get_team(team_id)
-                team.remove_membership(user_object)
-            except GithubException as ghe:
-                print(ghe)
-        else:
-            print("{} is not a member of org cannot delete".format(user))
-
-    if user and not delete:
-        if admin and is_member:
-            try:
-                team.add_membership(member=user_object, role="maintainer")
-            except GithubException as ghe:
-                print(ghe)
-        if admin and not is_member:
-            try:
-                org.invite_user(user=user_object, role="admin", teams=teams)
-            except GithubException as ghe:
-                print(ghe)
-
-        if not admin and is_member:
-            try:
-                team.add_membership(member=user_object, role="member")
-            except GithubException as ghe:
-                print(ghe)
-
-        if not admin and not is_member:
-            try:
-                org.invite_user(user=user_object, teams=teams)
-            except GithubException as ghe:
-                print(ghe)
+    helper_user_github(ctx, organization, user, team, delete, admin)
 
 
+github_cli.add_command(submit_pr)
+github_cli.add_command(votes)
 github_cli.add_command(list)
 github_cli.add_command(createteam)
 github_cli.add_command(createrepo)
-github_cli.add_command(modifyrepo)
+github_cli.add_command(updaterepo)
 github_cli.add_command(user)
index 31fcf54..5d4dc91 100644 (file)
@@ -18,6 +18,8 @@ from pygerrit2 import GerritRestAPI
 import ruamel.yaml
 import yaml
 
+from lftools.github_helper import prvotes
+
 log = logging.getLogger(__name__)
 
 
@@ -130,18 +132,29 @@ def sync_committers(ctx, id, info_file, ldap_file, repo):
 
 @click.command(name='check-votes')
 @click.argument('info_file')
-@click.argument('gerrit_url')
-@click.argument('change_number')
+@click.argument('endpoint', type=str)
+@click.argument('change_number', type=int)
 @click.option('--tsc', type=str, required=False,
               help='path to TSC INFO file')
+@click.option('--github_repo', type=str, required=False,
+              help='Provide github repo to Check against a Github Change')
 @click.pass_context
-def check_votes(ctx, info_file, gerrit_url, change_number, tsc):
+def check_votes(ctx, info_file, endpoint, change_number, tsc, github_repo):
     """Check votes on an INFO.yaml change.
 
-    Check for Majority of votes on a gerrit patchset
+    Check for Majority of votes on a gerrit or github patchset
     which changes an INFO.yaml file.
+
+    For Gerrit endpoint is the gerrit url
+    For Github the enpoint is the organization name
+
+    Examples:
+    lftools infofile check-votes /tmp/test/INFO.yaml lfit-sandbox 18 --github_repo test
+
+    lftools infofile check-votes ~/lf/allrepos/onosfw/INFO.yaml https://gerrit.opnfv.org/gerrit/ 67302
+
     """
-    def main(ctx, info_file, gerrit_url, change_number, tsc, majority_of_committers):
+    def main(ctx, info_file, endpoint, change_number, tsc, github_repo, majority_of_committers):
         """Function so we can iterate into TSC members after commiter vote has happend."""
         with open(info_file) as file:
             try:
@@ -150,32 +163,36 @@ def check_votes(ctx, info_file, gerrit_url, change_number, tsc):
                 log.error(exc)
 
         committer_info = info_data['committers']
-
         info_committers = []
-        for count, item in enumerate(committer_info):
-            committer = committer_info[count]['id']
-            info_committers.append(committer)
-
-        rest = GerritRestAPI(url=gerrit_url)
-        changes = rest.get("changes/{}/reviewers".format(change_number))
 
         info_change = []
-        for change in changes:
-            line = (change['username'], change['approvals']['Code-Review'])
 
-            if '+1' in line[1] or '+2' in line[1]:
-                info_change.append(change['username'])
+        if github_repo:
+            id = 'github_id'
+            githubvotes = prvotes(endpoint, github_repo, change_number)
+            for vote in githubvotes:
+                info_change.append(vote)
+
+        else:
+            id = 'id'
+            rest = GerritRestAPI(url=endpoint)
+            changes = rest.get("changes/{}/reviewers".format(change_number))
+            for change in changes:
+                line = (change['username'], change['approvals']['Code-Review'])
+                if '+1' in line[1] or '+2' in line[1]:
+                    info_change.append(change['username'])
+
+        for count, item in enumerate(committer_info):
+            committer = committer_info[count][id]
+            info_committers.append(committer)
 
         have_not_voted = [item for item in info_committers if item not in info_change]
         have_not_voted_length = (len(have_not_voted))
-
         have_voted = [item for item in info_committers if item in info_change]
         have_voted_length = (len(have_voted))
-
         log.info("Number of Committers:")
         log.info(len(info_committers))
         committer_lenght = (len(info_committers))
-
         log.info("Committers that have voted:")
         log.info(have_voted)
         log.info(have_voted_length)
@@ -189,8 +206,7 @@ def check_votes(ctx, info_file, gerrit_url, change_number, tsc):
 
         if (have_voted_length != 0):
             majority = (committer_lenght / have_voted_length)
-
-            if (majority == 1):
+            if (majority >= 1):
                 log.info("Majority committer vote reached")
                 if (tsc):
                     log.info("Need majority of tsc")
@@ -199,13 +215,12 @@ def check_votes(ctx, info_file, gerrit_url, change_number, tsc):
                     if majority_of_committers == 2:
                         log.info("TSC majority reached auto merging commit")
                     else:
-                        main(ctx, info_file, gerrit_url, change_number, tsc, majority_of_committers)
+                        main(ctx, info_file, endpoint, change_number, tsc, github_repo,  majority_of_committers)
             else:
                 log.info("majority not yet reached")
                 sys.exit(1)
-
     majority_of_committers = 0
-    main(ctx, info_file, gerrit_url, change_number, tsc, majority_of_committers)
+    main(ctx, info_file, endpoint, change_number, tsc, github_repo, majority_of_committers)
 
 
 infofile.add_command(get_committers)
index f13eecd..1f54cf5 100755 (executable)
@@ -71,12 +71,14 @@ def create_group(ctx, group):
 @click.command()
 @click.argument('info_file')
 @click.argument('group')
+@click.option('--githuborg', type=str, required=False,
+              help='github org name')
 @click.option('--noop', is_flag=True, required=False,
               help='show what would be changed')
 @click.pass_context
-def match_ldap_to_info(ctx, info_file, group, noop):
-    """Match an LDAP groups membership to an INFO.yaml file."""
-    helper_match_ldap_to_info(info_file, group, noop)
+def match_ldap_to_info(ctx, info_file, group, githuborg, noop):
+    """Match an LDAP or GITHUB group membership to an INFO.yaml file."""
+    helper_match_ldap_to_info(info_file, group, githuborg, noop)
 
 
 lfidapi.add_command(search_members)
diff --git a/lftools/github_helper.py b/lftools/github_helper.py
new file mode 100755 (executable)
index 0000000..4b73775
--- /dev/null
@@ -0,0 +1,223 @@
+# SPDX-License-Identifier: EPL-1.0
+##############################################################################
+# Copyright (c) 2019 The Linux Foundation and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+##############################################################################
+"""Github stub."""
+
+from __future__ import print_function
+
+import logging
+import sys
+
+from github import Github
+from github import GithubException
+
+from lftools import config
+
+log = logging.getLogger(__name__)
+
+
+def helper_list(ctx, organization, repos, audit, full, teams, team, repofeatures):
+    """List options for github org repos."""
+    token = config.get_setting("github", "token")
+    g = Github(token)
+    orgName = organization
+
+    try:
+        org = g.get_organization(orgName)
+    except GithubException as ghe:
+        log.error(ghe)
+
+    if repos:
+        log.info("All repos for organization: ", orgName)
+        repos = org.get_repos()
+        for repo in repos:
+            log.info(repo.name)
+
+    if audit:
+        log.info("{} members without 2fa:".format(orgName))
+        try:
+            members = org.get_members(filter_="2fa_disabled")
+        except GithubException as ghe:
+            log.error(ghe)
+        for member in members:
+            log.info(member.login)
+        log.info("{} outside collaborators without 2fa:".format(orgName))
+        try:
+            collaborators = org.get_outside_collaborators(filter_="2fa_disabled")
+        except GithubException as ghe:
+            log.error(ghe)
+        for collaborator in collaborators:
+            log.info(collaborator.login)
+
+    if repofeatures:
+        repos = org.get_repos()
+        for repo in repos:
+            log.info("{} wiki:{} issues:{}".format(repo.name, repo.has_wiki, repo.has_issues))
+            issues = repo.get_issues
+            for issue in issues():
+                log.info("{}".format(issue))
+
+    if full:
+        log.info("---")
+        log.info("#  All owners for {}:".format(orgName))
+        log.info("{}-owners:".format(orgName))
+
+        try:
+            members = org.get_members(role="admin")
+        except GithubException as ghe:
+            log.error(ghe)
+        for member in members:
+            log.info("  - '{}'".format(member.login))
+        log.info("#  All members for {}".format(orgName))
+        log.info("{}-members:".format(orgName))
+
+        try:
+            members = org.get_members()
+        except GithubException as ghe:
+            log.error(ghe)
+        for member in members:
+            log.info("  - '{}'".format(member.login))
+        log.info("#  All members and all teams for {}".format(orgName))
+
+        try:
+            teams = org.get_teams
+        except GithubException as ghe:
+            log.error(ghe)
+        for team in teams():
+            log.info("{}:".format(team.name))
+            for user in team.get_members():
+                log.info("  - '{}'".format(user.login))
+            log.info("")
+        teams = None
+
+    if teams:
+        try:
+            teams = org.get_teams
+        except GithubException as ghe:
+            log.error(ghe)
+        for team in teams():
+            log.info("{}".format(team.name))
+
+    if team:
+        try:
+            teams = org.get_teams
+        except GithubException as ghe:
+            log.error(ghe)
+
+        team_members = []
+
+        for t in teams():
+            if t.name == team:
+                log.info("{}".format(t.name))
+                for user in t.get_members():
+                    team_members.append(user.login)
+                    log.info("  - '{}'".format(user.login))
+
+        return(team_members)
+
+
+def prvotes(organization, repo, pr):
+    """Get votes on a github pr."""
+    token = config.get_setting("github", "token")
+    g = Github(token)
+    orgName = organization
+    try:
+        org = g.get_organization(orgName)
+    except GithubException as ghe:
+        log.error(ghe)
+
+    repo = org.get_repo(repo)
+    approval_list = []
+    author = repo.get_pull(pr).user.login
+    approval_list.append(author)
+
+    pr_mergable = repo.get_pull(pr).mergeable
+    log.info("MERGEABLE: {}".format(pr_mergable))
+
+    approvals = repo.get_pull(pr).get_reviews()
+    for approve in approvals:
+        if approve.state == ("APPROVED"):
+            approval_list.append(approve.user.login)
+    return(approval_list)
+
+
+def helper_user_github(ctx, organization, user, team, delete, admin):
+    """Add and Remove users from an org team."""
+    token = config.get_setting("github", "token")
+    g = Github(token)
+    orgName = organization
+    try:
+        org = g.get_organization(orgName)
+    except GithubException as ghe:
+        log.error(ghe)
+    try:
+        user_object = g.get_user(user)
+        log.info(user_object)
+    except GithubException as ghe:
+        log.error(ghe)
+        log.info("user {} not found".format(user))
+        sys.exit(1)
+    # check if user is a member
+    try:
+        is_member = org.has_in_members(user_object)
+        log.info("Is {} a member of org {}".format(user, is_member))
+    except GithubException as ghe:
+        log.error(ghe)
+    # get teams
+    try:
+        teams = org.get_teams
+    except GithubException as ghe:
+        log.error(ghe)
+
+    # set team to proper object
+    my_teams = [team]
+    this_team = [team for team in teams() if team.name in my_teams]
+    for t in this_team:
+        team_id = (t.id)
+    team = org.get_team(team_id)
+    teams = []
+    teams.append(team)
+
+    if delete:
+        if is_member:
+            try:
+                team.remove_membership(user_object)
+            except GithubException as ghe:
+                log.error(ghe)
+            log.info("Removing user {} from {}".format(user_object, team))
+        else:
+            log.info("{} is not a member of org cannot delete".format(user))
+            # TODO add revoke invite
+            log.info("Code does not handle revoking invitations.")
+
+    if user and not delete:
+        if admin and is_member:
+            try:
+                team.add_membership(member=user_object, role="maintainer")
+            except GithubException as ghe:
+                log.error(ghe)
+        if admin and not is_member:
+            try:
+                org.invite_user(user=user_object, role="admin", teams=teams)
+            except GithubException as ghe:
+                log.error(ghe)
+            log.info("Sending Admin invite to {} for {}".format(user_object, team))
+
+        if not admin and is_member:
+            try:
+                team.add_membership(member=user_object, role="member")
+            except GithubException as ghe:
+                log.error(ghe)
+
+        if not admin and not is_member:
+            try:
+                org.invite_user(user=user_object, teams=teams)
+            except GithubException as ghe:
+                log.error(ghe)
+            log.info("Sending invite to {} for {}".format(user_object, team))
index 1b69759..2bdeccc 100755 (executable)
@@ -18,6 +18,8 @@ import requests
 from six.moves import urllib
 import yaml
 
+from lftools.github_helper import helper_list
+from lftools.github_helper import helper_user_github
 from lftools.oauth2_helper import oauth_helper
 
 log = logging.getLogger(__name__)
@@ -110,42 +112,72 @@ def helper_create_group(group):
         log.debug(json.dumps(result, indent=4, sort_keys=True))
 
 
-def helper_match_ldap_to_info(info_file, group, noop):
-    """Helper only to be used in automation."""
+def helper_match_ldap_to_info(info_file, group, githuborg, noop):
+    """Helper matches ldap or github group to users in an info file.
+
+    Used in automation.
+    """
     with open(info_file) as file:
         try:
             info_data = yaml.safe_load(file)
         except yaml.YAMLError as exc:
             print(exc)
+    id = 'id'
+    if githuborg:
+        id = 'github_id'
+        ldap_data = helper_list(ctx=False, organization=githuborg, repos=False, audit=False,
+                                full=False, teams=False, repofeatures=False, team=group)
+    else:
+        ldap_data = helper_search_members(group)
 
-    ldap_data = helper_search_members(group)
     committer_info = info_data['committers']
 
     info_committers = []
     for count, item in enumerate(committer_info):
-        committer = committer_info[count]['id']
+        committer = committer_info[count][id]
         info_committers.append(committer)
 
     ldap_committers = []
-    for count, item in enumerate(ldap_data):
-        committer = ldap_data[count]['username']
-        ldap_committers.append(committer)
+    if githuborg:
+        for x in ldap_data:
+            committer = x
+            ldap_committers.append(committer)
+
+    else:
+        for count, item in enumerate(ldap_data):
+            committer = ldap_data[count]['username']
+            ldap_committers.append(committer)
 
     all_users = ldap_committers + info_committers
-    all_users.remove("lfservices_releng")
+
+    if not githuborg:
+        all_users.remove("lfservices_releng")
+
+    log.info("All users in org group")
     all_users = sorted(set(all_users))
+    for x in all_users:
+        log.info(x)
 
     for user in all_users:
         removed_by_patch = [item for item in ldap_committers if item not in info_committers]
         if (user in removed_by_patch):
             log.info("%s found in group %s " % (user, group))
             if noop is False:
-                log.info(" removing user %s from group %s" % (user, group))
-                helper_user(user, group, "--delete")
+                log.info("Removing user %s from group %s" % (user, group))
+                if githuborg:
+                    helper_user_github(ctx=False, organization=githuborg, user=user,
+                                       team=group, delete=True, admin=False)
+                else:
+                    helper_user(user, group, "--delete")
 
         added_by_patch = [item for item in info_committers if item not in ldap_committers]
         if (user in added_by_patch):
             log.info("%s not found in group %s" % (user, group))
             if noop is False:
-                log.info(" adding user %s to group %s" % (user, group))
-                helper_user(user, group, "")
+                log.info("Adding user %s to group %s" % (user, group))
+                if githuborg:
+                    helper_user_github(ctx=False, organization=githuborg, user=user,
+                                       team=group, delete=False, admin=False)
+
+                else:
+                    helper_user(user, group, "")
diff --git a/releasenotes/notes/github-1e99906af8ef75ac.yaml b/releasenotes/notes/github-1e99906af8ef75ac.yaml
new file mode 100644 (file)
index 0000000..15f8d44
--- /dev/null
@@ -0,0 +1,12 @@
+---
+features:
+  - |
+    --team now lists members of a specific team
+
+fixes:
+  - |
+    Fixes invite to team
+
+adds:
+  - |
+    prvotes counts votes on a github pull request
diff --git a/releasenotes/notes/infofile-4dec08c571b39df8.yaml b/releasenotes/notes/infofile-4dec08c571b39df8.yaml
new file mode 100644 (file)
index 0000000..c3655bf
--- /dev/null
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    check_votes now takes click.option('--github_repo')
+    Used in automation to determine is 50% of committers
+    have voted on an INFO.yaml change
diff --git a/releasenotes/notes/lfidapi-d597e5ef08fda9f7.yaml b/releasenotes/notes/lfidapi-d597e5ef08fda9f7.yaml
new file mode 100644 (file)
index 0000000..eec7bdc
--- /dev/null
@@ -0,0 +1,7 @@
+---
+adds:
+    - |
+           --githuborg
+           lfidapi info match-ldap-to-info now works for github groups.
+           expects group to be called $reponame-committers
+           id field for github users is github_id