Github api tools 64/15264/30
authorAric Gardner <agardner@linuxfoundation.org>
Thu, 11 Apr 2019 20:35:20 +0000 (16:35 -0400)
committerAric Gardner <agardner@linuxfoundation.org>
Mon, 10 Jun 2019 19:53:41 +0000 (15:53 -0400)
create-repo [OPTIONS] ORGANIZATION REPOSITORY DESCRIPTION
Create a Github repo within an Organization.
By default has_issues has_wiki and has_projects are set to false. See
--help to create a repo with these enabled.

modify-repo [OPTIONS] ORGANIZATION REPOSITORY
Modify a Github repo within an Organization.
By default has_issues has_wiki and has_projects are set to false. See
--help to use this command to enable these options

create-team [OPTIONS] ORGANIZATION NAME REPO PRIVACY
Create a Github team within an Organization.

list         List an Organization's GitHub repos.
  --audit          list members without 2fa
  --repos          list all repos
  --full           All members and their respective teams
  --teams          List avaliable teams
  --repofeatures   list enabled features for repos in an org
  --help           Show this message and exit.

user [OPTIONS] ORGANIZATION USER TEAM
Add and remove users
  --delete  remove user from a team within an org
  --admin   user is admin for org
  --help    Show this message and exit.

Github token is read from
~/.config/lftools/lftools.ini
In the form of:

[github]
token =

ISSUE: RELENG-1526
Change-Id: I558eb11897b555d26fac5abacb660d4bdb630a12
Signed-off-by: Aric Gardner <agardner@linuxfoundation.org>
.coafile
docs/commands/github.rst [new file with mode: 0644]
docs/commands/index.rst
lftools/cli/__init__.py
lftools/cli/github_cli.py [new file with mode: 0644]
releasenotes/notes/github-create-334e11334f8b38ff.yaml [new file with mode: 0644]
requirements.txt

index 6d0a517..07cc80b 100644 (file)
--- a/.coafile
+++ b/.coafile
@@ -48,7 +48,9 @@ known_third_party_imports =
     shade,
     xdg,
     yaml,
-    pygerrit2
+    pygerrit2,
+    pygithub
+
 pydocstyle_ignore = D203, D213, D301
 max_line_length = 120
 
diff --git a/docs/commands/github.rst b/docs/commands/github.rst
new file mode 100644 (file)
index 0000000..37bd08e
--- /dev/null
@@ -0,0 +1,37 @@
+******
+GITHUB
+******
+
+.. program-output:: lftools github --help
+
+Commands
+========
+
+create-repo
+-----------
+
+.. program-output:: lftools github create-repo --help
+
+create-team
+-----------
+
+.. program-output:: lftools github create-team --help
+
+
+list
+----
+
+.. program-output:: lftools github list --help
+
+user
+----
+
+.. program-output:: lftools github user --help
+
+API requires an [github] section in ~/.config/lftools/lftools.ini:
+
+.. code-block:: bash
+
+   [github]
+   token = REDACTED
+
index 9947d04..a0032c7 100644 (file)
@@ -14,6 +14,7 @@ It supports the following commands:
     deploy
     dco
     gerrit
+    github
     infofile
     lfidapi
     license
index 4bca34c..b1fd934 100644 (file)
@@ -23,6 +23,7 @@ from lftools.cli.config import config_sys
 from lftools.cli.dco import dco
 from lftools.cli.deploy import deploy
 from lftools.cli.gerrit import gerrit_cli
+from lftools.cli.github_cli import github_cli
 from lftools.cli.infofile import infofile
 from lftools.cli.jenkins import jenkins_cli
 from lftools.cli.lfidapi import lfidapi
@@ -80,6 +81,7 @@ cli.add_command(config_sys)
 cli.add_command(deploy)
 cli.add_command(dco)
 cli.add_command(gerrit_cli, name='gerrit')
+cli.add_command(github_cli, name='github')
 cli.add_command(infofile)
 cli.add_command(jenkins_cli, name='jenkins')
 cli.add_command(license)
diff --git a/lftools/cli/github_cli.py b/lftools/cli/github_cli.py
new file mode 100644 (file)
index 0000000..4a2d782
--- /dev/null
@@ -0,0 +1,345 @@
+# 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 tools."""
+
+from __future__ import print_function
+
+import sys
+
+import click
+from github import Github
+from github import GithubException
+
+from lftools import config
+
+
+@click.group()
+@click.pass_context
+def github_cli(ctx):
+    """GITHUB TOOLS."""
+    pass
+
+
+@click.command(name='list')
+@click.argument('organization')
+@click.option('--audit', is_flag=True, required=False,
+              help='List members without 2fa')
+@click.option('--repos', is_flag=True, required=False,
+              help='List all repos')
+@click.option('--full', is_flag=True, required=False,
+              help='All members and their respective teams')
+@click.option('--teams', is_flag=True, required=False,
+              help='List avaliable teams')
+@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))
+
+
+@click.command(name='create-repo')
+@click.argument('organization')
+@click.argument('repository')
+@click.argument('description')
+@click.option('--has_issues', is_flag=True, required=False,
+              help='Repo should have issues')
+@click.option('--has_projects', is_flag=True, required=False,
+              help='Repo should have projects')
+@click.option('--has_wiki', is_flag=True, required=False,
+              help='Repo should have wiki')
+@click.pass_context
+def createrepo(ctx, organization, repository, description, has_issues, has_projects, has_wiki):
+    """Create a Github repo within an Organization.
+
+    By default has_issues has_wiki and has_projects is set to false.
+    See --help to create a repo with these enabled.
+    """
+    token = config.get_setting("github", "token")
+    g = Github(token)
+    orgName = organization
+    has_issues = has_issues or False
+    has_wiki = has_wiki or False
+    has_projects = has_projects or False
+    print("Creating repo under organization: ", orgName)
+    try:
+        org = g.get_organization(orgName)
+    except GithubException as ghe:
+        print(ghe)
+    repos = org.get_repos()
+    for repo in repos:
+        if repo.name == repository:
+            print("repo already exists")
+            sys.exit(1)
+    try:
+        org.create_repo(
+            repository,
+            allow_rebase_merge=False,
+            auto_init=False,
+            description=description,
+            has_issues=has_issues,
+            has_projects=has_projects,
+            has_wiki=has_wiki,
+            private=False,
+        )
+    except GithubException as ghe:
+        print(ghe)
+
+
+@click.command(name='modify-repo')
+@click.argument('organization')
+@click.argument('repository')
+@click.option('--has_issues', is_flag=True, required=False,
+              help='Repo should have issues')
+@click.option('--has_projects', is_flag=True, required=False,
+              help='Repo should have projects')
+@click.option('--has_wiki', is_flag=True, required=False,
+              help='Repo should have wiki')
+@click.pass_context
+def modifyrepo(ctx, organization, repository, has_issues, has_projects, has_wiki):
+    """Modify 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.
+    """
+    token = config.get_setting("github", "token")
+    g = Github(token)
+    orgName = organization
+
+    has_issues = has_issues or False
+    has_wiki = has_wiki or False
+    has_projects = has_projects or False
+
+    try:
+        org = g.get_organization(orgName)
+    except GithubException as ghe:
+        print(ghe)
+
+    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)
+
+
+@click.command(name='create-team')
+@click.argument('organization')
+@click.argument('name')
+@click.argument('repo')
+@click.argument('privacy')
+@click.pass_context
+def createteam(ctx, organization, name, repo, privacy):
+    """Create a Github team within an Organization."""
+    token = config.get_setting("github", "token")
+    g = Github(token)
+    orgName = organization
+    print("Creating team {} for repo {} under organization {} ".format(name, repo, orgName))
+    try:
+        org = g.get_organization(orgName)
+    except GithubException as ghe:
+        print(ghe)
+
+    try:
+        repos = org.get_repos
+    except GithubException as ghe:
+        print(ghe)
+
+    try:
+        teams = org.get_teams
+    except GithubException as ghe:
+        print(ghe)
+
+    my_repos = [repo]
+    repos = [repo for repo in repos() if repo.name in my_repos]
+    for repo in repos:
+        print(repo)
+
+    if repos:
+        print("repo found")
+    else:
+        print("repo not found")
+        sys.exit(1)
+
+    for team in teams():
+        if team.name == name:
+            print("team {} already exists".format(team))
+            sys.exit(1)
+    try:
+        org.create_team(
+            name=name,
+            repo_names=repos,
+            privacy=privacy
+        )
+    except GithubException as ghe:
+        print(ghe)
+
+
+@click.command(name='user')
+@click.argument('organization')
+@click.argument('user')
+@click.argument('team')
+@click.option('--delete', is_flag=True, required=False,
+              help='Remove user from org')
+@click.option('--admin', is_flag=True, required=False,
+              help='User is admin for org, or a maintaner of a team')
+@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)
+
+
+github_cli.add_command(list)
+github_cli.add_command(createteam)
+github_cli.add_command(createrepo)
+github_cli.add_command(modifyrepo)
+github_cli.add_command(user)
diff --git a/releasenotes/notes/github-create-334e11334f8b38ff.yaml b/releasenotes/notes/github-create-334e11334f8b38ff.yaml
new file mode 100644 (file)
index 0000000..5b07360
--- /dev/null
@@ -0,0 +1,19 @@
+---
+features:
+  - |
+    Github list and create repositories.
+
+    Usage: Usage: lftools github [OPTIONS] COMMAND [ARGS]...
+
+
+    .. code-block:: none
+
+       Commands:
+           audit   List Users for an Org that do not have 2fa enabled.
+           create  Create a Github repo for within an Organizations.
+           list    List and Organizations GitHub repos.
+
+    .. code-block:: none
+
+       Options:
+         --help    Show this message and exit.
index 8e4b15b..4a14546 100644 (file)
@@ -11,6 +11,7 @@ python-jenkins~=1.1.0
 tqdm
 xdg~=1.0.7;python_version<'3'
 xdg~=3.0.0;python_version>='3'
+pygithub
 httplib2
 email_validator
 oauth2client