Code development platform for open source projects from the European Union institutions

Skip to content
Snippets Groups Projects
redmine_leankit_create_issue 17 KiB
Newer Older
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from __future__ import absolute_import, print_function

import os
import sys

import configparser as ConfigParser
import logging
import argparse
import traceback
import shlex
import re
from subprocess import check_output, STDOUT, CalledProcessError

hostname = os.uname()[1]
whoami = sys._getframe().f_code.co_name
script = os.path.basename(__file__).split(".")[0]

for pylib in list(sys.path):
    if '/usr/local/lib' in pylib:
        sys.path.remove(pylib)
        continue
    elif '/export/home/snet/.local' in pylib:
        sys.path.remove(pylib)
        continue

''' BASE CONFIG '''
global_iniFile = '/opt/etc/ini/global.ini'
config_global = ConfigParser.RawConfigParser()
config_global.optionxform(str())
config_global.optionxform = str
config_global.read(global_iniFile)
sys.path.append(config_global.get('APPLICATION', 'PYTHON-LIBRARY'))

from redminelib import Redmine
import snet.sloggly

sys.path.append('/opt/auth/library')
import leankit

try:
    logger = snet.sloggly.setup_custom_logger(script, logging.INFO)
except Exception as e:
    print("\n=======\n")
    title = "Something went wrong. Please inform SS team."
    msg = "Error initializing the snet logger: " + str(e) + " :: " + traceback.format_exc(5)
    print(msg)
    whoami = sys._getframe().f_code.co_name
    messages = [hostname, script, title, whoami, msg]
    traceback.print_exc()
    sys.exit(1)


def obj_dump(obj):
    '''
      Object dumper
    '''
    for attr in dir(obj):
        if '_' in attr:
            continue
        try:
            print("obj.%s = %s" % (attr, str(getattr(obj, attr))))
        except:  # noqa: E722
            print("obj.%s = %s" % (attr, getattr(obj, attr)))

    for name, data in inspect.getmembers(obj):
        if inspect.isclass(data):
            print('name:%s' % (name))
            print(data)


def obj_dump_r(obj, level=0, deepth=2):
    '''
       Recursive Object dumper
    '''
    for attr in dir(obj):
        if '_' in attr:
            continue
        try:
            msg = 2 * level * ' '
            msg += ("obj.%s = %s" % (attr, str(getattr(obj, attr))))
            print(msg)
        except:  # noqa: E722
            msg = 2 * level * ' '
            msg += ("obj.%s = %s" % (attr, getattr(obj, attr)))
            print(msg)

        if level >= deepth:
            continue

        try:
            obj_dump_r(getattr(obj, attr), level=level + 1)
        except:  # noqa: E722
            msg = 2 * level * ' '
            msg = 2 * level * ' '
            msg += "Dump is stinking... crashed."
            print(msg)


if 'http_proxy' in os.environ:
    del os.environ['http_proxy']
if 'https_proxy' in os.environ:
    del os.environ['https_proxy']

CA_bundle = '/usr/local/share/ca-certificates/snetroot/SNetRootCA_device_bundle.pem'
CA_bundle = '/etc/ssl/certs/ca-certificates.crt'

basedir = '/opt/SNet/scm'

Tracker_List = ['Bug', 'Feature', 'Service Improvement']
leankit_tracker_conversion = dict()
leankit_tracker_conversion['Bug'] = 'Defect'
leankit_tracker_conversion['Feature'] = 'Task'

parser = argparse.ArgumentParser()
parser.add_argument('-p', '--project', type=str, default='local',
                    help='The project to restrict: ALL for all',
                    dest='project')
parser.add_argument('-t', '--title', '--title', type=str, required=True,
                    help='The issue title and description',
                    dest='title')
parser.add_argument('-o', '--owner', type=str, required=True,
                    help='The owner of the new issue',
                    dest='owner')
parser.add_argument('--type', type=str, required=True, choices=Tracker_List,
                    help='The type of issue', dest='tracker')
parser.add_argument('--leankit-id', type=int, required=False,
                    help='The leankit card id', dest='leankit_id')
parser.add_argument('--leankit-url', type=str, required=False,
                    help='The leankit card url id', dest='leankit_url')
parser.add_argument('--leankit-create', action="store_true", default=False,
                    help='The leankit create a card', dest='leankit_create')
parser.add_argument('--leankit-board', type=str, required=False,
                    help='The leankit board name for the card creation', dest='leankit_board')
parser.add_argument('--leankit-board-id', type=int, required=False,
                    help='The leankit board id for the card creation', dest='leankit_board_id')
args = parser.parse_args()

# leankit validation:
if (args.leankit_id is not None or args.leankit_url is not None) and (args.leankit_create is True or args.leankit_board is not None or args.leankit_board_id is not None):
    logger.error("leankit_id is provided with one of th option leankit_create leankit_board leankit_board_id.")
    logger.error("This is incompatible.")
    print(parser.format_help())
    sys.exit(1)

if args.leankit_id is None and args.leankit_url is None and args.leankit_create is False:
    logger.error("leankit_id is not provided, but no card creation requested.")
    logger.error("This is incompatible.")
    print(parser.format_help())
    sys.exit(1)

if args.leankit_id is None and args.leankit_url is None and args.leankit_create is True and args.leankit_board is None and args.leankit_board_id is None:
    logger.error("leankit_id is not provided, the leankit_create requested, but the leankit_board or leankit_board_id is not provided.")
    logger.error("This is incompatible.")
    print(parser.format_help())
    sys.exit(1)

if args.leankit_id is None and args.leankit_url is None and args.leankit_create is True and args.leankit_board is not None and args.leankit_board_id is not None:
    logger.error("leankit_id is not provided, the leankit_create requested, but the leankit_board and leankit_board_id is provided.")
    logger.error("This is incompatible.")
    print(parser.format_help())
    sys.exit(1)

leankit_board_id = None
leankit_card_id = None
leankit_card_type_id = None
leankit_user_id = None
leankit_board_lane_id = None

if args.leankit_url is not None:
    leankit_card_id = str(args.leankit_url).replace('https://globalntt.leankit.com/card/', '')
    # https://globalntt.leankit.com/card/31512091272763

if args.leankit_id is not None:
    leankit_card_id = str(args.leankit_id)

if leankit_card_id is not None:
    leankit_card_res = leankit.get_card(leankit_card_id)
    print(leankit_card_res)
    print(json_dumps(leankit_card_res, sort_keys=True, indent=4))
    if leankit_card_res['actualFinish'] is not None:
        logger.error("Card is finished, so this is not possible")
        sys.exit(1)
    if leankit_card_res['lane']['laneClassType'] == "archive":
        logger.error("Card is finished, so this is not possible")
        sys.exit(1)

    # check assigned
    '''

        "assignedUsers": [
                {
                    "avatar": "https://globalntt.leankit.com/avatar/show/31512085826382/?s=25",
                    "emailAddress": "David.VERNAZOBRES@ext.ec.europa.eu",
                    "firstName": "David",
                    "fullName": "David VERNAZOBRES",
                    "id": "31512085826382",
                    "lastName": "VERNAZOBRES"
                }
            ],

        {
          "cardIds": ["945202295", "945233018"],
          "userIdsToAssign": ["478440842", "583458214"],
          "wipOverrideComment": "This is needed if user WIP is violated on a board"
        }
    '''

if args.leankit_create is True and args.leankit_board_id is not None:
    leankit_board_id = args.leankit_board_id

elif args.leankit_create is True and args.leankit_board is not None:
    leankit_res = leankit.get_board(args.leankit_board)
    # print(leankit_res)
    '''
        {'pageMeta': {'totalRecords': 2, 'offset': 0, 'limit': 100, 'startRow': 1, 'endRow': 2}, 'boards': [{'id': '31512088856393', 'title': 'DEV', 'description': '', 'boardRoleId': 4, 'isWelcome': False, 'boardRole': 'boardAdministrator', 'level': {'id': '31512085971730', 'depth': 3, 'maxDepth': 3, 'label': 'Team', 'color': '#ff841f'}}, {'id': '31512088544453', 'title': 'DEV-NMS3-phasein', 'description': '', 'boardRoleId': 4, 'isWelcome': False, 'boardRole': 'boardAdministrator'}]}
    '''
    found = False
    for bb in leankit_res['boards']:
        if bb['title'] == args.leankit_board:
            found = True
            leankit_board_id = bb['id']

    if found is False:
        logger.error("Leankit board '%s': id is not found. Check the board name parameter." % args.leankit_board)
        sys.exit(1)

if args.leankit_create is True:
    leankit_res = leankit.get_cardtype_from_board(leankit_board_id)
    # print(leankit_res)
    # print(json_dumps(leankit_res, sort_keys=True, indent=4))
    if args.tracker not in leankit_tracker_conversion:
        logger.error("%s is not a valid leankit type conversion." % (args.tracker))
        sys.exit(1)
    for tt in leankit_res['cardTypes']:
        if tt['name'] == leankit_tracker_conversion[args.tracker]:
            leankit_card_type_id = tt['id']

    if leankit_card_type_id is None:
        logger.error("%s is not found in the leankit card type id." % (args.tracker))
        sys.exit(1)

    # check lane
    # leankit_board_lane_id
    leankit_res = leankit.get_board_detail(leankit_board_id)
    # print(json_dumps(leankit_res, sort_keys=True, indent=4))
    for ll in leankit_res['lanes']:
        if ll['name'] == "In Process":
            # print(ll)
            leankit_board_lane_id = ll['id']

    if leankit_board_lane_id is None:
        logger.error('Lane in process is not found')
        sys.exit(1)

# project_filter = 'ALL'
project_filter = args.project

redmine_config_global = ConfigParser.RawConfigParser()
redmine_config_global.read(config_global.get('INI', 'Redmine'))

# Parse config
REDMINE_HOSTNAME = redmine_config_global.get('GLOBAL', 'HOST')
REDMINE_PROTO = redmine_config_global.get('GLOBAL', 'PROTO')
REDMINE_KEY = redmine_config_global.get('CREDENTIAL', 'APIkey')
REDMINE_WS_KEY = redmine_config_global.get('GLOBAL', 'WS_KEY')
REDMINE_VERSION = redmine_config_global.get('GLOBAL', 'VERSION')
redmine = None

if project_filter == 'local':
    # cmd = ("pwd")
    # cmd = ("hg config paths.default || git config --get remote.origin.url")
    cmds = []
    # cmds.append('git rev-parse --show-toplevel 2>/dev/null || hg root 2>/dev/null || echo "$PWD"')
    # cmds.append('git rev-parse --show-toplevel || hg root || echo "$PWD"')
    # cmds.append("hg config paths.default || git config --get remote.origin.url")
    cmds.append("hg config paths.default")
    cmds.append("git config --get remote.origin.url")

    vcs = ''
    for cmd in cmds:
        # print("hg-git cmd: %s" % (cmd))
        # print("hg-git cmd: %s" % (shlex.split(cmd)))

        try:
            output = check_output((shlex.split(cmd)),
                                  stderr=None,
                                  shell=True)
        except CalledProcessError as ex:
            output = ex.output

        # print("hg-git output: '%s'" % (output))
        # print('-----------------')
        # continue

        if output == '':
            continue
        if ':/' in output and '.git' in output:
            vcs = 'git'
            break
        elif '//' in output:
            vcs = 'hg'
            break
    # sys.exit(1)

    if vcs == '':
        print("bad repo: %s, abort." % (output))
        sys.exit(1)
    if vcs == 'hg':
        match = re.search(r'^.*/(/.*)$', output)
    elif vcs == 'git':
        match = re.search(r'^.*:(/.*)$', output)
    if match:
        repo = match.group(1)
    else:
        print("bad repo: %s, abort." % (output))
        sys.exit(1)

    print("repo: %s" % (repo))

    redmine = Redmine('%s://%s' % (REDMINE_PROTO, REDMINE_HOSTNAME),
                      key=REDMINE_KEY, requests={'verify': CA_bundle},
                      version=REDMINE_VERSION)

    red_projects = redmine.project.all(offset=0, limit=1000, include='repositories')
    for u in red_projects:
        # print(u)
        # print('%s' % (u.id))
        # print('%s' % (u.name))
        # print('%s' % (u.identifier))
        if len(u.repositories) == 0:
            continue

        elif str(repo) == u.repositories[0]['url']:
            print("found project %s" % (u.name))
            project_filter = u.name
            break

if redmine is None:
    redmine = Redmine('%s://%s' % (REDMINE_PROTO, REDMINE_HOSTNAME),
                      key=REDMINE_KEY, requests={'verify': CA_bundle},
                      version=REDMINE_VERSION)

status_id = None
statuses = redmine.issue_status.all()
for st in statuses:
    # print(st.id, st)
    if str(st) == 'Assigned':
        # print(st.id, st)
        status_id = st.id
        break

if status_id is None:
    print("status not found.")
    sys.exit(1)

tracker_id = None
trackers = redmine.tracker.all()
for tr in trackers:
    # print(tr.id, tr)
    if str(tr) == args.tracker:
        # print(tr.id, tr)
        tracker_id = tr.id
        break

if tracker_id is None:
    print("tracker not found.")
    sys.exit(1)

# priority_id = None
#  priorities = redmine.priority.all()
# for pr in priorities:
#    print(pr.id, pr)
#    continue
#    if str(pr) == args.priority:
#        # print(tr.id, tr)
#        priority_id = tr.id
#        break

# if priority_id is None:
#    print("priority not found.")
#    sys.exit(1)

# user = redmine.user.get(args.owner)
user_id = None
user_email = None
users = redmine.user.filter(name=args.owner)
for usr in users:
    # print(usr.id, usr, usr.login)
    if str(usr.login) == args.owner:
        # print(usr.id, usr)
        user_id = usr.id
        user_email = usr.mail
        break

if user_id is None:
    logger.error("user not found.")
    sys.exit(1)

# print(user_email)

leankit_res = leankit.get_user(user_email)
# print(leankit_res)
if leankit_res['pageMeta']['totalRecords'] != 1:
    logger.error('user is not found uniquely in leankit')
    sys.exit(1)

leankit_user_id = leankit_res['users'][0]['id']

    # check assigned
    '''

        "assignedUsers": [
                {
                    "avatar": "https://globalntt.leankit.com/avatar/show/31512085826382/?s=25",
                    "emailAddress": "David.VERNAZOBRES@ext.ec.europa.eu",
                    "firstName": "David",
                    "fullName": "David VERNAZOBRES",
                    "id": "31512085826382",
                    "lastName": "VERNAZOBRES"
                }
            ],

        {
          "cardIds": ["945202295", "945233018"],
          "userIdsToAssign": ["478440842", "583458214"],
          "wipOverrideComment": "This is needed if user WIP is violated on a board"
        }
        leankit.card_assign
        leankit_user_id

    '''
    if leankit_card_res['assignedUsers'] is None:
        logger.error('missing assigned user check')
        sys.exit(1)

    assignee = False
    logger.info(leankit_user_id)
    for lean_user in leankit_card_res['assignedUsers']:
        print(json_dumps(lean_user, sort_keys=True, indent=4))
        if lean_user['id'] == leankit_user_id:
            assignee = True
            break
    if assignee is False:
        logger.error('leankit card is not assigned to the current user.')
        sys.exit(1)

# issues = redmine.issue.filter(status_id='open', sort='project:asc,id:asc')
# >>> project = redmine.project.get('vacation')
# >>> project.issues
issue = redmine.issue.new()
issue.project_id = project_filter
issue.subject = args.title
issue.description = args.title
issue.tracker_id = tracker_id
issue.status_id = status_id
# issue.priority_id = 7
issue.assigned_to_id = user_id
issue.save()

redmine_id = str(issue.id)

if args.leankit_create is True:
    leankit_card = dict()
    leankit_card["boardId"] = str(leankit_board_id)
    leankit_card["title"] = args.title
    leankit_card["typeId"] = leankit_card_type_id
    leankit_card["assignedUserIds"] = list()
    leankit_card["assignedUserIds"].append(leankit_user_id)
    leankit_card["description"] = args.title
    leankit_card["laneId"] = leankit_board_lane_id
    leankit_card["priority"] = "normal"
    leankit_card["size"] = 1
    t = dict()
    t["fieldId"] = "31512088868633"
    t["value"] = int(redmine_id)

    # print(json_dumps(leankit_card, sort_keys=True, indent=4))
    leankit_res = leankit.create_card(leankit_card)
    print(leankit_res)

    leankit_id = leankit_res['id']
    print(leankit_id)

else:
    # if leankit_card_id is not None:
    leankit_path = [
            {"op": "replace",
             "path": "/customFields/0",
             "value": {
                 "fieldId": "31512088868633",
                 "value": int(redmine_id)
                 }
             }]
    leankit_res = leankit.update_card(leankit_card_id, leankit_path)
    print(leankit_res)
    leankit_id = leankit_card_id

issue = redmine.issue.get(redmine_id)
issue.custom_fields = [{'id': 41, 'value': leankit_card_id}]
issue.save()
print('#Redmine %s' % (redmine_id))
print('#Leankit %s' % (leankit_id))
print('%s lk:%s fixes #%s' % (str(args.title), str(leankit_id), redmine_id))