#!/opt/gvenv/venv_leankit/bin/python3 #!/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 inspect import traceback import shlex import re from subprocess import check_output, STDOUT, CalledProcessError from json import dumps as json_dumps 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/') import library.leankit as 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'}]} ''' if leankit_res is None: logger.error("Leankit board '%s': id is not found. Check the board name parameter." % args.leankit_board) sys.exit(1) leankit_board_id = leankit_res['id'] 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'] if leankit_card_id is not None: # 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 leankit_card["customFields"] = list() t = dict() t["fieldId"] = "31512088868633" t["value"] = int(redmine_id) leankit_card["customFields"].append(t) # 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(int(redmine_id)) # print('redmine_issue:' + str(issue)) issue.custom_fields = [{'id': 41, 'value': leankit_id}] issue.save() # print('redmine_issue:' + str(issue.custom_fields)) # print(issue.custom_field.all()) # print(issue.custom_fields.get(41)) print('#Redmine %s' % (redmine_id)) print('#Leankit %s' % (leankit_id)) print('%s lk:%s fixes #%s' % (str(args.title), str(leankit_id), redmine_id)) sys.exit(0)