From: Aric Gardner Date: Thu, 11 Apr 2019 20:35:20 +0000 (-0400) Subject: Github api tools X-Git-Tag: v0.25.0~7^2 X-Git-Url: https://gerrit.linuxfoundation.org/infra/gitweb?a=commitdiff_plain;h=f03e4f5d6adc6950c984173017d32409d8f2fb6b;p=releng%2Flftools.git Github api tools 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 --- diff --git a/.coafile b/.coafile index 6d0a517e..07cc80bd 100644 --- 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 index 00000000..37bd08e1 --- /dev/null +++ b/docs/commands/github.rst @@ -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 + diff --git a/docs/commands/index.rst b/docs/commands/index.rst index 9947d04a..a0032c77 100644 --- a/docs/commands/index.rst +++ b/docs/commands/index.rst @@ -14,6 +14,7 @@ It supports the following commands: deploy dco gerrit + github infofile lfidapi license diff --git a/lftools/cli/__init__.py b/lftools/cli/__init__.py index 4bca34c4..b1fd9343 100644 --- a/lftools/cli/__init__.py +++ b/lftools/cli/__init__.py @@ -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 index 00000000..4a2d7824 --- /dev/null +++ b/lftools/cli/github_cli.py @@ -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 index 00000000..5b073609 --- /dev/null +++ b/releasenotes/notes/github-create-334e11334f8b38ff.yaml @@ -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. diff --git a/requirements.txt b/requirements.txt index 8e4b15b8..4a145463 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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