зеркало из
1
0
Форкнуть 0
ghas-jira-integration/cli.py

322 строки
9.9 KiB
Python
Исходник Обычный вид История

2021-01-21 13:51:16 +03:00
import argparse
import ghlib
import jiralib
import os
import sys
import json
import util
from sync import Sync, DIRECTION_G2J, DIRECTION_J2G, DIRECTION_BOTH
2021-01-28 00:23:13 +03:00
import logging
2021-02-08 16:20:12 +03:00
import server
2021-12-02 02:53:01 +03:00
import anticrlf
2021-01-21 13:51:16 +03:00
2021-01-28 00:23:13 +03:00
root = logging.getLogger()
root.setLevel(logging.DEBUG)
handler = logging.StreamHandler(sys.stdout)
2021-12-02 02:53:01 +03:00
handler.setFormatter(anticrlf.LogFormatter("%(levelname)s:%(name)s:%(message)s"))
2021-01-28 00:23:13 +03:00
handler.setLevel(logging.DEBUG)
root.addHandler(handler)
2021-01-21 13:51:16 +03:00
2021-01-21 13:51:16 +03:00
def fail(msg):
print(msg)
sys.exit(1)
def direction_str_to_num(dstr):
2021-08-17 00:48:57 +03:00
if dstr == "gh2jira":
return DIRECTION_G2J
2021-08-17 00:48:57 +03:00
elif dstr == "jira2gh":
return DIRECTION_J2G
2021-08-17 00:48:57 +03:00
elif dstr == "both":
return DIRECTION_BOTH
else:
fail('Unknown direction argument "{direction}"!'.format(direction=dstr))
2021-01-21 13:51:16 +03:00
def serve(args):
if not args.gh_url or not args.jira_url:
2021-08-17 00:48:57 +03:00
fail("Both GitHub and JIRA URL have to be specified!")
if not args.gh_token:
2021-08-17 00:48:57 +03:00
fail("No GitHub token specified!")
if not args.jira_user or not args.jira_token:
2021-08-17 00:48:57 +03:00
fail("No JIRA credentials specified!")
if not args.jira_project:
2021-08-17 00:48:57 +03:00
fail("No JIRA project specified!")
if not args.secret:
2021-08-17 00:48:57 +03:00
fail("No Webhook secret specified!")
github = ghlib.GitHub(args.gh_url, args.gh_token)
jira = jiralib.Jira(args.jira_url, args.jira_user, args.jira_token)
s = Sync(
github,
2021-09-03 03:10:48 +03:00
jira.getProject(args.jira_project, args.jira_labels),
2021-08-18 08:04:27 +03:00
direction=direction_str_to_num(args.direction),
)
server.run_server(s, args.secret, port=args.port)
2021-01-21 13:51:16 +03:00
def sync(args):
if not args.gh_url or not args.jira_url:
2021-08-17 00:48:57 +03:00
fail("Both GitHub and JIRA URL have to be specified!")
2021-01-21 13:51:16 +03:00
if not args.gh_token:
2021-08-17 00:48:57 +03:00
fail("No GitHub credentials specified!")
2021-01-21 13:51:16 +03:00
if not args.jira_user or not args.jira_token:
2021-08-17 00:48:57 +03:00
fail("No JIRA credentials specified!")
2021-01-21 13:51:16 +03:00
if not args.jira_project:
2021-08-17 00:48:57 +03:00
fail("No JIRA project specified!")
2021-01-21 13:51:16 +03:00
if not args.gh_org:
2021-08-17 00:48:57 +03:00
fail("No GitHub organization specified!")
2021-01-21 13:51:16 +03:00
if not args.gh_repo:
2021-08-17 00:48:57 +03:00
fail("No GitHub repository specified!")
2021-01-21 13:51:16 +03:00
github = ghlib.GitHub(args.gh_url, args.gh_token)
2021-01-21 13:51:16 +03:00
jira = jiralib.Jira(args.jira_url, args.jira_user, args.jira_token)
2021-08-17 00:48:57 +03:00
jira_project = jira.getProject(
2021-08-18 08:04:27 +03:00
args.jira_project,
args.issue_end_state,
args.issue_reopen_state,
2021-09-03 03:10:48 +03:00
args.jira_labels,
2021-08-17 00:48:57 +03:00
)
repo_id = args.gh_org + "/" + args.gh_repo
if args.state_file:
if args.state_issue:
2021-08-17 00:48:57 +03:00
fail("--state-file and --state-issue are mutually exclusive!")
state = util.state_from_file(args.state_file)
elif args.state_issue:
state = jira_project.fetch_repo_state(repo_id, args.state_issue)
else:
state = {}
2021-08-17 00:48:57 +03:00
s = Sync(github, jira_project, direction=direction_str_to_num(args.direction))
s.sync_repo(repo_id, states=state)
2021-01-21 13:51:16 +03:00
if args.state_file:
util.state_to_file(args.state_file, state)
elif args.state_issue:
jira_project.save_repo_state(repo_id, state, args.state_issue)
2021-01-21 13:51:16 +03:00
def check_hooks(args):
pass
2021-01-21 13:51:16 +03:00
def install_hooks(args):
if not args.hook_url:
2021-08-17 00:48:57 +03:00
fail("No hook URL specified!")
2021-01-21 13:51:16 +03:00
if not args.secret:
2021-08-17 00:48:57 +03:00
fail("No hook secret specified!")
2021-01-21 13:51:16 +03:00
if not args.gh_url and not args.jira_url:
2021-08-17 00:48:57 +03:00
fail("Neither GitHub nor JIRA URL specified!")
2021-01-21 13:51:16 +03:00
# user wants to install a github hook
if args.gh_url:
if not args.gh_token:
2021-08-17 00:48:57 +03:00
fail("No GitHub token specified!")
2021-01-21 13:51:16 +03:00
if not args.gh_org:
2021-08-17 00:48:57 +03:00
fail("No GitHub organization specified!")
2021-01-21 13:51:16 +03:00
github = ghlib.GitHub(args.gh_url, args.gh_token)
2021-01-21 13:51:16 +03:00
if args.gh_repo:
2021-08-17 00:48:57 +03:00
ghrepo = github.getRepository(args.gh_org + "/" + args.gh_repo)
ghrepo.create_hook(url=args.hook_url, secret=args.secret)
2021-01-21 13:51:16 +03:00
else:
github.create_org_hook(url=args.hook_url, secret=args.secret)
2021-01-21 13:51:16 +03:00
# user wants to install a JIRA hook
if args.jira_url:
if not args.jira_user or not args.jira_token:
2021-08-17 00:48:57 +03:00
fail("No JIRA credentials specified!")
2021-01-21 13:51:16 +03:00
jira = jiralib.Jira(args.jira_url, args.jira_user, args.jira_token)
2021-08-17 00:48:57 +03:00
jira.create_hook("github_jira_synchronization_hook", args.hook_url, args.secret)
2021-01-21 13:51:16 +03:00
def list_hooks(args):
if not args.gh_url and not args.jira_url:
2021-08-17 00:48:57 +03:00
fail("Neither GitHub nor JIRA URL specified!")
2021-01-21 13:51:16 +03:00
# user wants to list github hooks
if args.gh_url:
if not args.gh_token:
2021-08-17 00:48:57 +03:00
fail("No GitHub token specified!")
2021-01-21 13:51:16 +03:00
if not args.gh_org:
2021-08-17 00:48:57 +03:00
fail("No GitHub organization specified!")
2021-01-21 13:51:16 +03:00
github = ghlib.GitHub(args.gh_url, args.gh_token)
2021-01-21 13:51:16 +03:00
if args.gh_repo:
2021-08-17 00:48:57 +03:00
for h in github.getRepository(
args.gh_org + "/" + args.gh_repo
).list_hooks():
2021-01-21 13:51:16 +03:00
print(json.dumps(h, indent=4))
else:
for h in github.list_org_hooks(args.gh_org):
print(json.dumps(h, indent=4))
# user wants to list JIRA hooks
if args.jira_url:
if not args.jira_user or not args.jira_token:
2021-08-17 00:48:57 +03:00
fail("No JIRA credentials specified!")
2021-01-21 13:51:16 +03:00
jira = jiralib.Jira(args.jira_url, args.jira_user, args.jira_token)
for h in jira.list_hooks():
print(json.dumps(h, indent=4))
def main():
credential_base = argparse.ArgumentParser(add_help=False)
2021-08-17 00:48:57 +03:00
credential_base.add_argument("--gh-org", help="GitHub organization")
credential_base.add_argument("--gh-repo", help="GitHub repository")
2021-01-21 13:51:16 +03:00
credential_base.add_argument(
2021-08-17 00:48:57 +03:00
"--gh-url",
help="API URL of GitHub instance",
2021-01-21 13:51:16 +03:00
)
credential_base.add_argument(
2021-08-17 00:48:57 +03:00
"--gh-token",
help="GitHub API token. Alternatively, the GH2JIRA_GH_TOKEN may be set.",
default=os.getenv("GH2JIRA_GH_TOKEN"),
2021-01-21 13:51:16 +03:00
)
2021-08-17 00:48:57 +03:00
credential_base.add_argument("--jira-url", help="URL of JIRA instance")
credential_base.add_argument("--jira-user", help="JIRA user name")
2021-01-21 13:51:16 +03:00
credential_base.add_argument(
2021-08-17 00:48:57 +03:00
"--jira-token",
help="JIRA password. Alternatively, the GH2JIRA_JIRA_TOKEN may be set.",
default=os.getenv("GH2JIRA_JIRA_TOKEN"),
2021-01-21 13:51:16 +03:00
)
2021-08-17 00:48:57 +03:00
credential_base.add_argument("--jira-project", help="JIRA project key")
2021-09-03 03:10:48 +03:00
credential_base.add_argument("--jira-labels", help="JIRA bug label(s)")
2021-08-12 23:16:23 +03:00
credential_base.add_argument(
2021-08-18 08:04:27 +03:00
"--secret",
help="Webhook secret. Alternatively, the GH2JIRA_SECRET may be set.",
default=os.getenv("GH2JIRA_SECRET"),
)
direction_base = argparse.ArgumentParser(add_help=False)
direction_base.add_argument(
2021-08-17 00:48:57 +03:00
"--direction",
help='Sync direction. Possible values are "gh2jira" (alert states have higher priority than issue states),'
2021-08-17 00:48:57 +03:00
+ '"jira2gh" (issue states have higher priority than alert states) and "both" (adjust in both directions)',
default="both",
)
2021-01-21 13:51:16 +03:00
issue_state_base = argparse.ArgumentParser(add_help=False)
issue_state_base.add_argument(
2021-08-17 00:48:57 +03:00
"--issue-end-state",
help="Custom end state (e.g. Closed) Done by default",
default="Done",
)
issue_state_base.add_argument(
2021-08-17 00:48:57 +03:00
"--issue-reopen-state",
help="Custom reopen state (e.g. In Progress) To Do by default",
default="To Do",
)
2021-01-21 13:51:16 +03:00
2021-08-17 00:48:57 +03:00
parser = argparse.ArgumentParser(prog="gh2jira")
2021-01-21 13:51:16 +03:00
subparsers = parser.add_subparsers()
# serve
serve_parser = subparsers.add_parser(
2021-08-17 00:48:57 +03:00
"serve",
parents=[credential_base, direction_base, issue_state_base],
2021-08-17 00:48:57 +03:00
help="Spawn a webserver which keeps GitHub alerts and JIRA tickets in sync",
description="Spawn a webserver which keeps GitHub alerts and JIRA tickets in sync",
2021-01-21 13:51:16 +03:00
)
serve_parser.add_argument(
2021-08-17 00:48:57 +03:00
"--port", help="The port the server will listen on", default=5000
)
serve_parser.set_defaults(func=serve)
2021-01-21 13:51:16 +03:00
# sync
sync_parser = subparsers.add_parser(
2021-08-17 00:48:57 +03:00
"sync",
parents=[credential_base, direction_base, issue_state_base],
2021-08-17 00:48:57 +03:00
help="Synchronize GitHub alerts and JIRA tickets for a given repository",
description="Synchronize GitHub alerts and JIRA tickets for a given repository",
2021-01-21 13:51:16 +03:00
)
sync_parser.add_argument(
2021-08-17 00:48:57 +03:00
"--state-file",
help="File holding the current states of all alerts. The program will create the"
+ " file if it doesn't exist and update it after each run.",
default=None,
)
sync_parser.add_argument(
2021-08-17 00:48:57 +03:00
"--state-issue",
help="The key of the issue holding the current states of all alerts. The program "
+ 'will create the issue if "-" is given as the argument. The issue will be '
+ "updated after each run.",
default=None,
)
2021-01-21 13:51:16 +03:00
sync_parser.set_defaults(func=sync)
# hooks
hooks = subparsers.add_parser(
2021-08-17 00:48:57 +03:00
"hooks",
help="Manage JIRA and GitHub webhooks",
description="Manage JIRA and GitHub webhooks",
2021-01-21 13:51:16 +03:00
)
hooks_subparsers = hooks.add_subparsers()
# list hooks
hooks_list = hooks_subparsers.add_parser(
2021-08-17 00:48:57 +03:00
"list",
2021-01-21 13:51:16 +03:00
parents=[credential_base],
2021-08-17 00:48:57 +03:00
help="List existing GitHub or JIRA webhooks",
description="List existing GitHub or JIRA webhooks",
2021-01-21 13:51:16 +03:00
)
hooks_list.set_defaults(func=list_hooks)
# install hooks
hooks_install = hooks_subparsers.add_parser(
2021-08-17 00:48:57 +03:00
"install",
2021-01-21 13:51:16 +03:00
parents=[credential_base],
2021-08-17 00:48:57 +03:00
help="Install existing GitHub or JIRA webhooks",
description="Install GitHub or JIRA webhooks",
2021-01-21 13:51:16 +03:00
)
2021-08-17 00:48:57 +03:00
hooks_install.add_argument("--hook-url", help="Webhook target url")
2021-01-21 13:51:16 +03:00
hooks_install.add_argument(
2021-08-17 00:48:57 +03:00
"--insecure-ssl",
action="store_true",
help="Install GitHub hook without SSL check",
2021-01-21 13:51:16 +03:00
)
hooks_install.set_defaults(func=install_hooks)
# check hooks
hooks_check = hooks_subparsers.add_parser(
2021-08-17 00:48:57 +03:00
"check",
2021-01-21 13:51:16 +03:00
parents=[credential_base],
2021-08-17 00:48:57 +03:00
help="Check that hooks are installed properly",
description="Check that hooks are installed properly",
2021-01-21 13:51:16 +03:00
)
hooks_check.set_defaults(func=check_hooks)
def print_usage(args):
print(parser.format_usage())
parser.set_defaults(func=print_usage)
args = parser.parse_args()
# run the given action
args.func(args)
2021-08-17 00:48:57 +03:00
2021-01-21 13:51:16 +03:00
main()