import argparse import ghlib import jiralib import os import sys import json import util from sync import Sync, DIRECTION_G2J, DIRECTION_J2G, DIRECTION_BOTH import logging import server import anticrlf root = logging.getLogger() root.setLevel(logging.DEBUG) handler = logging.StreamHandler(sys.stdout) handler.setFormatter(anticrlf.LogFormatter("%(levelname)s:%(name)s:%(message)s")) handler.setLevel(logging.DEBUG) root.addHandler(handler) def fail(msg): print(msg) sys.exit(1) def direction_str_to_num(dstr): if dstr == "gh2jira": return DIRECTION_G2J elif dstr == "jira2gh": return DIRECTION_J2G elif dstr == "both": return DIRECTION_BOTH else: fail('Unknown direction argument "{direction}"!'.format(direction=dstr)) def serve(args): if not args.gh_url or not args.jira_url: fail("Both GitHub and JIRA URL have to be specified!") if not args.gh_token: fail("No GitHub token specified!") if not args.jira_user or not args.jira_token: fail("No JIRA credentials specified!") if not args.jira_project: fail("No JIRA project specified!") if not args.secret: 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, jira.getProject(args.jira_project, args.jira_labels), direction=direction_str_to_num(args.direction), ) server.run_server(s, args.secret, port=args.port) def sync(args): if not args.gh_url or not args.jira_url: fail("Both GitHub and JIRA URL have to be specified!") if not args.gh_token: fail("No GitHub credentials specified!") if not args.jira_user or not args.jira_token: fail("No JIRA credentials specified!") if not args.jira_project: fail("No JIRA project specified!") if not args.gh_org: fail("No GitHub organization specified!") if not args.gh_repo: fail("No GitHub repository specified!") github = ghlib.GitHub(args.gh_url, args.gh_token) jira = jiralib.Jira(args.jira_url, args.jira_user, args.jira_token) jira_project = jira.getProject( args.jira_project, args.issue_end_state, args.issue_reopen_state, args.jira_labels, ) repo_id = args.gh_org + "/" + args.gh_repo if args.state_file: if args.state_issue: 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 = {} s = Sync(github, jira_project, direction=direction_str_to_num(args.direction)) s.sync_repo(repo_id, states=state) 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) def check_hooks(args): pass def install_hooks(args): if not args.hook_url: fail("No hook URL specified!") if not args.secret: fail("No hook secret specified!") if not args.gh_url and not args.jira_url: fail("Neither GitHub nor JIRA URL specified!") # user wants to install a github hook if args.gh_url: if not args.gh_token: fail("No GitHub token specified!") if not args.gh_org: fail("No GitHub organization specified!") github = ghlib.GitHub(args.gh_url, args.gh_token) if args.gh_repo: ghrepo = github.getRepository(args.gh_org + "/" + args.gh_repo) ghrepo.create_hook(url=args.hook_url, secret=args.secret) else: github.create_org_hook(url=args.hook_url, secret=args.secret) # user wants to install a JIRA hook if args.jira_url: if not args.jira_user or not args.jira_token: fail("No JIRA credentials specified!") jira = jiralib.Jira(args.jira_url, args.jira_user, args.jira_token) jira.create_hook("github_jira_synchronization_hook", args.hook_url, args.secret) def list_hooks(args): if not args.gh_url and not args.jira_url: fail("Neither GitHub nor JIRA URL specified!") # user wants to list github hooks if args.gh_url: if not args.gh_token: fail("No GitHub token specified!") if not args.gh_org: fail("No GitHub organization specified!") github = ghlib.GitHub(args.gh_url, args.gh_token) if args.gh_repo: for h in github.getRepository( args.gh_org + "/" + args.gh_repo ).list_hooks(): 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: fail("No JIRA credentials specified!") 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) credential_base.add_argument("--gh-org", help="GitHub organization") credential_base.add_argument("--gh-repo", help="GitHub repository") credential_base.add_argument( "--gh-url", help="API URL of GitHub instance", ) credential_base.add_argument( "--gh-token", help="GitHub API token. Alternatively, the GH2JIRA_GH_TOKEN may be set.", default=os.getenv("GH2JIRA_GH_TOKEN"), ) credential_base.add_argument("--jira-url", help="URL of JIRA instance") credential_base.add_argument("--jira-user", help="JIRA user name") credential_base.add_argument( "--jira-token", help="JIRA password. Alternatively, the GH2JIRA_JIRA_TOKEN may be set.", default=os.getenv("GH2JIRA_JIRA_TOKEN"), ) credential_base.add_argument("--jira-project", help="JIRA project key") credential_base.add_argument("--jira-labels", help="JIRA bug label(s)") credential_base.add_argument( "--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( "--direction", help='Sync direction. Possible values are "gh2jira" (alert states have higher priority than issue states),' + '"jira2gh" (issue states have higher priority than alert states) and "both" (adjust in both directions)', default="both", ) issue_state_base = argparse.ArgumentParser(add_help=False) issue_state_base.add_argument( "--issue-end-state", help="Custom end state (e.g. Closed) Done by default", default="Done", ) issue_state_base.add_argument( "--issue-reopen-state", help="Custom reopen state (e.g. In Progress) To Do by default", default="To Do", ) parser = argparse.ArgumentParser(prog="gh2jira") subparsers = parser.add_subparsers() # serve serve_parser = subparsers.add_parser( "serve", parents=[credential_base, direction_base, issue_state_base], 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", ) serve_parser.add_argument( "--port", help="The port the server will listen on", default=5000 ) serve_parser.set_defaults(func=serve) # sync sync_parser = subparsers.add_parser( "sync", parents=[credential_base, direction_base, issue_state_base], help="Synchronize GitHub alerts and JIRA tickets for a given repository", description="Synchronize GitHub alerts and JIRA tickets for a given repository", ) sync_parser.add_argument( "--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( "--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, ) sync_parser.set_defaults(func=sync) # hooks hooks = subparsers.add_parser( "hooks", help="Manage JIRA and GitHub webhooks", description="Manage JIRA and GitHub webhooks", ) hooks_subparsers = hooks.add_subparsers() # list hooks hooks_list = hooks_subparsers.add_parser( "list", parents=[credential_base], help="List existing GitHub or JIRA webhooks", description="List existing GitHub or JIRA webhooks", ) hooks_list.set_defaults(func=list_hooks) # install hooks hooks_install = hooks_subparsers.add_parser( "install", parents=[credential_base], help="Install existing GitHub or JIRA webhooks", description="Install GitHub or JIRA webhooks", ) hooks_install.add_argument("--hook-url", help="Webhook target url") hooks_install.add_argument( "--insecure-ssl", action="store_true", help="Install GitHub hook without SSL check", ) hooks_install.set_defaults(func=install_hooks) # check hooks hooks_check = hooks_subparsers.add_parser( "check", parents=[credential_base], help="Check that hooks are installed properly", description="Check that hooks are installed properly", ) 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) main()