コード例 #1
0
class JiraInstance:
    """
    Class which is response for creataing FixVersion, marking it as RELEASED and for annote tasks which where
    done within lastday.
    """
    def __init__(self, config):
        self.SERVER_ADDRESS = config["SERVER_ADDRESS"]
        self.USER = config["USER"]
        self.PASSWORD = config["PASSWORD"]
        self.PROJECT = config["PROJECT_TAG"]
        self.CHANGELOG_TAG = config["CHANGELOG_TAG"]
        self.tasks = []
        self.fixedVersion = None

        options = {
            'server': self.SERVER_ADDRESS
        }

        self.jira = JIRA(options, basic_auth=(self.USER, self.PASSWORD))

    def is_any_new_changeset_in_changelog(self) -> bool:
        """
        Check if there is any new changeset in changelog in Jira task
        """
        changelog_issue = self.jira.issue(self.CHANGELOG_TAG, fields='comment')
        changelog = self.jira.comments(changelog_issue)[-1]
        regexp = "{}-([1-9]+[0-9]*)".format(self.PROJECT)
        for line in changelog.body.splitlines():
            issueTag = re.search(regexp, line)
            if issueTag is None or issueTag is "":
                continue
            self.tasks.append(issueTag.group())

        return True if len(self.tasks) > 0 else False

    def add_fixed_version_to_tasks(self, fixed_version):
        """
        Create and add fixed version to tasks from chagnelog task pointed in conf.json
        """
        if fixed_version is None or fixed_version is "":
            print("Wrong fixed_version")
            return

        today = datetime.date.today()
        today_str = today.strftime('%Y-%m-%d')
        self.jira.create_version(name=(fixed_version), project=self.PROJECT, released=True, startDate=today_str,
                                 releaseDate=today_str)

        for task in self.tasks:
            print("Add fixed version to {}".format(task))
            issue = self.jira.issue(task, fields='fixVersions')
            issue.add_field_value('fixVersions', {'name': fixed_version})
コード例 #2
0
def changejira(project,buildtime,judgement,newversion,releasejira):
    jira = JIRA('http://jira.ccdomain.com',basic_auth=('jira', 'jira@2017~!@'))
    if releasejira == "":
        jiralist = judgelist(project,buildtime,judgement)
    else:
        jiralist = comparelist(project,buildtime,releasejira)
    jirareq_list= searchjirareq(project,buildtime)
    #创建version:
    curTime = datetime.date.today()
    curTime = curTime.strftime("%Y-%m-%d")
    try:
        jira.create_version(newversion,project,description="Pollux %s 版本转测试" %newversion,startDate=curTime)
    except BaseException:
        print "create version %s error,maybe it's existed" %(newversion)
    #将所有组织测试的单转成回归测试
    for a in jiralist:
        print "start to change bug %s to 回归测试" %(str(a))
        try:
            issue = jira.issue(a)
            #修改 jira 单中的修复版本:
            issue_dict = {
                'fixVersions':[{'name':newversion}]
            }
            issue.update(fields=issue_dict)
            #修改 jira 单状态:
            jira.transition_issue(issue,'191',comment="自动回归测试")
            #修改指向人
            newassign = str(issue.fields.reporter.name)
            jira.assign_issue(issue,newassign)
        except BaseException:
            print "change fail in %s" %(str(a))
    for b in jirareq_list:
        print "start to change req %s to 回归测试" %(str(b))
        try:
            issue = jira.issue(b)
            #修改 jira 单中的修复版本:
            issue_dict = {
                'fixVersions':[{'name':newversion}]
            }
            issue.update(fields=issue_dict)
            #修改 jira 单状态:
            jira.transition_issue(issue,'21',comment="需求自动回归测试")
        except BaseException:
            print "change fail in %s" %(str(b))
コード例 #3
0
ファイル: main.py プロジェクト: nonamenix/sync_jira_versions
def create_versions(jira: JIRA, project: str, versions: t.List[Version]):
    if len(versions) == 0:
        click.echo("\nThere is no versions for creation")
    else:
        click.echo("\nVersions for creation:")

        for version in versions:
            click.echo("- {}".format(version.name))

        if click.confirm(
            "Do you want to create this versions in {project} project?".format(
                project=project
            )
        ):

            with click.progressbar(versions) as versions_bar:
                for version in versions_bar:
                    jira.create_version(
                        name=version.name,
                        project=project,
                        description=description_template.format(
                            original_version_id=version.id
                        ),
                    )
コード例 #4
0
# Creacion de versiones
for month in range(12):
    month = month + 1
    days = 30
    if month in [1, 3, 5, 7, 8, 10, 12]:
        days = 31
    if month == 2:
        days = 29
    prefix = ""
    if month < 10:
        prefix = "0"
    monthStr = "{0}{1}".format(prefix, month)
    version = "{0}.{1}".format(year,monthStr)
    endDate = "{0}{1}-{2}-{3}".format("20", year, monthStr, days)
    createVersions and jira.create_version(version, projectKey, releaseDate=endDate)


# Creacion de componentes
for component in componentsToCreate(projectKey, componentsList):
    createComponents and jira.create_component(component, projectKey)
    print("------------>> " + component)

# Eliminacion de componentes
for myComponent in gettingComponentes(projectKey):
    deleteComponents and jira.delete_component(myComponent.id)

# Obtener descripcion aleatoria
def getIssueDescription():
    return " ".join([random.choice(i) for i in l])
コード例 #5
0
class JiraClient(object):
    """
    Helper class for the JIRA
    """

    def __init__(self, url, username, password):
        """
        :param url:
        :param username:
        :param password:
        :return:
        """
        self.url = url
        self.webhook_url = self.url.strip('/') + '/rest/webhooks/1.0/webhook'
        self.basic_auth = (username, password)
        self.client = JIRA(url, basic_auth=self.basic_auth)

    def __str__(self):
        return '{} {}'.format(self.__class__.__name__, self.url)

################
#   Accounts   #
################

    def groups(self):
        """
        :return:
        """
        return self.client.groups()

    def users(self, json_result=True):
        """
        :param json_result:
        :return:
        """
        return self._search_users('_', json_result=json_result)

    def users_by_email(self, email, json_result=True):
        """
        :param email:
        :param json_result:
        :return:
        """
        return self._search_users(email, json_result=json_result)

    def users_by_group(self, name):
        """
        :param name:
        :return:
        """
        try:
            return self.client.group_members(name)
        except JIRAError as exc:
            logger.warning(exc)
            return dict()

    def _search_users(self, qstr, json_result=True):
        """
        :param qstr:
        :param json_result:
        :return:
        """
        def _user_dict_format(user):  # Note: Keep the consistent return format with the users_by_group method
            return {'key':      user.key,
                    'active':   user.active,
                    'fullname': user.displayName,
                    'email':    user.emailAddress
                    }

        users = self.client.search_users(qstr)

        if json_result:  # Note: Keep the consistent return format with the users_by_group method
            return [_user_dict_format(user) for user in users]
        else:
            return users

#############
#  Project  #
#############

    def create_project(self, key):
        """
        :param key:
        :return:
        """
        return self.client.create_project(key)

    def get_project(self, key, json_result=True):
        """
        :param key:
        :param json_result:
        :return:
        """
        if json_result:
            return self.client.project(key).raw
        else:
            return self.client.project(key)

    def get_projects(self, json_result=True):
        """
        :param json_result:
        :return:
        """
        project_objs = self.client.projects()
        if json_result:
            return [_each.raw for _each in project_objs]
        else:
            return project_objs

    def delete_project(self, key):
        """
        :param key:
        :return:
        """
        return self.client.delete_project(key)

#############
#  Version  #
#############

    def get_project_versions(self, name, json_result=True):
        """
        :param name: project name
        :param json_result:
        :return:
        """
        try:
            version_objs = self.client.project_versions(name)
            if json_result:
                return [_each.name for _each in version_objs]
            else:
                return version_objs
        except Exception as exc:
            logger.warn(exc)
            return []

    def create_project_version(self, name, project_name, **kwargs):
        """
        :param name: version name
        :param project_name: project name
        :param kwargs:
        :return:
        """
        return self.client.create_version(name, project_name, **kwargs)

#############
#   fields  #
########### #

    def get_fields(self):
        """
        :return:
        """
        return self.client.fields()

    def get_non_custom_fields(self):
        """
        :return:
        """
        return [each for each in self.client.fields() if not each.get('custom', True)]

    def get_custom_fields(self):
        """
        :return:
        """
        return [each for each in self.client.fields() if each.get('custom', True)]

    def get_field_id_by_name(self, name):
        """
        :param name:
        :return:
        """
        ids = [each['id'] for each in self.client.fields() if each.get('name', '') == name]
        if ids:
            return ids[0]
        else:
            return None

    def get_field_id_for_hours_left(self):
        """
        :return:
        """
        # For Argo customized field
        name = 'Hrs Left'
        return self.get_field_id_by_name(name)

############
#  issues  #
############

    def get_issue(self, name, json_result=True):
        """
        :param name:
        :param json_result:
        :return:
        """
        try:
            issue_obj = self.client.issue(name)
        except JIRAError as exc:
            logger.warn('Not found: %s', exc)
            return None

        if json_result:
            issue_dict = copy.deepcopy(issue_obj.raw['fields'])
            issue_dict['url'] = issue_obj.self
            issue_dict['id'] = issue_obj.id
            issue_dict['key'] = issue_obj.key
            # Replace custom field name
            return issue_dict
        else:
            return issue_obj

    def add_fix_version_to_issue(self, issue_name, version_name, issuetype=None):
        """
        :param issue_name:
        :param version_name:
        :param issuetype:
        :return:
        """
        return self._add_versions_from_issue(issue_name, version_name, issuetype=issuetype, _version_type='fixVersions')

    def add_affected_version_to_issue(self, issue_name, version_name, issuetype=None):
        """
        :param issue_name:
        :param version_name:
        :param issuetype:
        :return:
        """
        return self._add_versions_from_issue(issue_name, version_name, issuetype=issuetype, _version_type='versions')

    def _add_versions_from_issue(self, issue_name, version_name, issuetype=None, _version_type='fixVersions'):
        """
        :param issue_name:
        :param version_name:
        :param issuetype:
        :param _version_type:
        :return:
        """
        assert _version_type in ['fixVersions', 'versions'], 'Unsupported version type'

        issue_obj = self.get_issue(issue_name, json_result=False)

        if not issue_obj:
            return None

        if issuetype is not None and issue_obj.fields.issuetype.name.lower() != issuetype.lower():
            logger.info('SKIP. The issue type is %s, expected issue type is %s',
                        issue_obj.fields.issuetype.name.lower(),
                        issuetype.lower())
            return None

        logger.info('Update issue %s, with %s %s', issue_obj.key, _version_type, version_name)
        ret = issue_obj.add_field_value(_version_type, {'name': version_name})
        issue_obj.update()
        return ret

    def remove_affected_versions_from_issue(self, issue_name, versions_to_remove):
        """
        :param issue_name:
        :param versions_to_remove:
        :return:
        """
        return self._remove_versions_from_issue(issue_name, versions_to_remove, _version_type='versions')

    def remove_fix_versions_from_issue(self, issue_name, versions_to_remove):
        """
        :param issue_name:
        :param versions_to_remove:
        :return:
        """
        return self._remove_versions_from_issue(issue_name, versions_to_remove, _version_type='fixVersions')

    def _remove_versions_from_issue(self, issue_name, versions_to_remove, _version_type='fixVersions'):
        """
        :param issue_name:
        :param versions_to_remove:
        :param _version_type:
        :return:
        """
        assert _version_type in ['fixVersions', 'versions'], 'Unsupported version type'
        if type(versions_to_remove) not in [list, tuple, set]:
            versions_to_remove = [versions_to_remove]
        versions = []
        issue_obj = self.get_issue(issue_name, json_result=False)
        for ver in getattr(issue_obj.fields, _version_type):
            if ver.name not in versions_to_remove:
                versions.append({'name': ver.name})
        issue_obj.update(fields={_version_type: versions})

    def create_issue(self, project, summary, description=None, issuetype='Bug', reporter=None, **kwargs):
        """
        :param project:
        :param summary:
        :param description:
        :param issuetype:
        :param reporter:
        :param kwargs:
        :return:
        """
        # {
        #     "fields": {
        #        "project":
        #        {
        #           "key": "TEST"
        #        },
        #        "summary": "Always do right. This will gratify some people and astonish the REST.",
        #        "description": "Creating an issue while setting custom field values",
        #        "issuetype": {
        #           "name": "Bug"
        #        },
        #        "customfield_11050" : {"Value that we're putting into a Free Text Field."}
        #    }
        # }

        fields_dict = dict()

        for k, v in kwargs.items():
            fields_dict[k] = v

        fields_dict['project'] = {'key': project}
        fields_dict['description'] = description or ''
        fields_dict['summary'] = summary
        fields_dict['issuetype'] = {'name': issuetype}

        if reporter:
            users = self.users_by_email(reporter, json_result=False)
            if users:
                fields_dict['reporter'] = {'name': users[0].name}

        return self.client.create_issue(fields=fields_dict)

    def update_issue(self, name, **kwargs):
        """
        :param name:
        :param kwargs:
        :return:
        """
        issue_obj = self.get_issue(name, json_result=False)
        issue_obj.update(**kwargs)

######################
#   issue comments   #
######################

    def get_issue_comments(self, issue_name, latest_num=5, json_result=True):
        """
        :param issue_name:
        :param latest_num:
        :param json_result:
        :return:
        """
        try:
            comments = self.client.comments(issue_name)
        except JIRAError as exc:
            logger.warn(exc)
            return []
        comments = comments[::-1][:latest_num]
        if json_result:
            return [each.raw for each in comments]
        else:
            return comments

    def add_issue_comment(self, issue_name, msg, commenter=None):
        """
        :param issue_name:
        :param msg:
        :param commenter:
        :return:
        """
        if commenter:
            users = self.users_by_email(commenter, json_result=False)
            if users:
                msg_header = 'The comment is created by {}({}) from AX system. \n\n'.\
                    format(users[0].displayName, users[0].emailAddress)
                msg = msg_header + msg
        return self.client.add_comment(issue_name, msg)

###############
#  issue type #
###############

    def get_issue_types(self, json_result=True):
        """
        :param json_result:
        :return:
        """
        objs = self.client.issue_types()
        if json_result:
            return [obj.raw for obj in objs]
        else:
            return objs

    def get_issue_type_by_name(self, name, json_result=True):
        """
        :param name:
        :param json_result:
        :return:
        """
        try:
            obj = self.client.issue_type_by_name(name)
        except KeyError as exc:
            logger.warn(exc)
            return None
        else:
            if json_result:
                return obj.raw
            else:
                return obj

#############
#   Query   #
#############

    def query_issues(self, **kwargs):
        """
        :param kwargs:
        :return:

        max_results: maximum number of issues to return. Total number of results
                     If max_results evaluates as False, it will try to get all issues in batches.
        json_result: JSON response will be returned when this parameter is set to True.
                     Otherwise, ResultList will be returned
        """
        SUPPORTED_KEYS = ('project', 'status', 'component', 'labels', 'issuetype', 'priority',
                          'creator', 'assignee', 'reporter', 'fixversion', 'affectedversion')
        max_results = kwargs.pop('max_results', 100)
        _json_result = kwargs.pop('json_result', False)
        jql_str_list = []
        for k, v in kwargs.items():
            if k not in SUPPORTED_KEYS:
                continue
            jql_str_list.append('{} = "{}"'.format(k.strip(), v.strip()))
        if jql_str_list:
            jql_str = ' AND '.join(jql_str_list)
        else:
            jql_str = ''  # Fetch ALL issues
        try:
            ret = self.client.search_issues(jql_str, maxResults=max_results, json_result=_json_result)
        except Exception as exc:
            logger.warn(exc)
            ret = {"issues": []}
        return ret

################
# Query Issues #
################

    def get_issues_by_project(self, project_name, **kwargs):
        """
        :param project_name:
        :param kwargs:
        :return:
        """
        return self.query_issues(project=project_name, **kwargs)

    def get_issues_by_component(self, component, **kwargs):
        """
        :param component:
        :param kwargs:
        :return:
        """
        return self.query_issues(component=component, **kwargs)

    def get_issues_by_assignee(self, assignee, **kwargs):
        """
        :param assignee:
        :param kwargs:
        :return:
        """
        return self.query_issues(assignee=assignee, **kwargs)

    def get_issues_by_status(self, status, **kwargs):
        """
        :param status:
        :param kwargs:
        :return:
        """
        return self.query_issues(status=status, **kwargs)

    def get_issues_by_label(self, labels, **kwargs):
        """
        :param labels:
        :param kwargs:
        :return:
        """
        return self.query_issues(labels=labels, **kwargs)

    def get_issues_by_fixversion(self, fix_version, **kwargs):
        """
        :param fix_version:
        :param kwargs:
        :return:
        """
        return self.query_issues(fixverion=fix_version, **kwargs)

    def get_issues_by_affectedversion(self, affected_version, **kwargs):
        """
        :param affected_version:
        :param kwargs:
        :return:
        """
        return self.query_issues(affectedversion=affected_version, **kwargs)

################
#  Query Hours #
################

    def get_total_hours(self, **kwargs):
        """
        :param kwargs:
        :return:
        """
        all_issues = self.query_issues(**kwargs)
        field_id = self.get_field_id_for_hours_left()
        hours = [getattr(iss_obj.fields, field_id) for iss_obj in all_issues]
        return sum([float(each) for each in hours if each])

    def get_total_hours_by_project(self, project_name):
        """
        :param project_name:
        :return:
        """
        return self.get_total_hours(project=project_name)

    def get_total_hours_by_component(self, component):
        """
        :param component:
        :return:
        """
        return self.get_total_hours(component=component)

    def get_total_hours_by_assignee(self, assignee):
        """
        :param assignee:
        :return:
        """
        return self.get_total_hours(assignee=assignee)

    def get_total_hours_by_label(self, label):
        """
        :param label:
        :return:
        """
        return self.get_total_hours(labels=label)

##############
#   webhook  #
##############

    def create_ax_webhook(self, url, projects=None):
        """Create AX Jira webhook
        :param url:
        :param projects:
        :return:
        """
        payload = copy.deepcopy(AX_JIRA_WEBHOOK_PAYLOAD)
        payload['name'] = payload['name'] + self._get_cluster_name()
        payload['url'] = url
        filter_dict = self._generate_project_filter(projects)
        logger.info('Webhook project filter is: %s', filter_dict)
        payload.update(filter_dict)
        return self._requests(self.webhook_url, 'post', data=payload)

    def get_ax_webhook(self):
        """Get AX Jira webhook
        :return:
        """
        response = self._requests(self.webhook_url, 'get')
        wh_name = AX_JIRA_WEBHOOK_PAYLOAD['name'] + self._get_cluster_name()
        ax_whs = [wh for wh in response.json() if wh['name'] == wh_name]
        if not ax_whs:
            logger.error('Could not get Jira webhook for this cluster: %s, ignore it', wh_name)
        else:
            return ax_whs[0]

    def update_ax_webhook(self, projects=None):
        """Update AX Jira webhook
        :param projects:
        :return:
        """
        ax_wh = self.get_ax_webhook()
        if ax_wh:
            filter_dict = self._generate_project_filter(projects)
            logger.info('Webhook project filter is: %s', filter_dict)
            logger.info('Update the webhook %s', ax_wh['self'])
            return self._requests(ax_wh['self'], 'put', data=filter_dict)
        else:
            logger.warn('Skip the webhook update')

    def delete_ax_webhook(self):
        """Delete AX Jira webhook
        :return: 
        """
        response = self._requests(self.webhook_url, 'get')
        wh_name = AX_JIRA_WEBHOOK_PAYLOAD['name'] + self._get_cluster_name()

        ax_whs = [wh for wh in response.json() if wh['name'] == wh_name]
        for wh in ax_whs:
            logger.info('Delete webhook %s', wh['self'])
            self._delete_webhook(url=wh['self'])

    def get_ax_webhooks(self):
         """Get all AX webhooks
         :return:
         """
         response = self._requests(self.webhook_url, 'get')
         webhooks = response.json()
         # filter out non-ax webhooks
         return [wh for wh in webhooks if wh['name'].startswith(AX_JIRA_WEBHOOK_PAYLOAD['name'])]

    def delete_ax_webhooks(self):
        """Delete all AX Jira webhooks
        :return:
        """
        ax_whs = self.get_ax_webhooks()
        for wh in ax_whs:
            logger.info('Delete webhook %s', wh['self'])
            self._delete_webhook(url=wh['self'])

    def _generate_project_filter(self, projects):
        """
        :param projects:
        :return:
        """
        if not projects:
            filter_str = ''
        else:
            project_filter_list = []
            project_objs = self.get_projects(json_result=False)
            for pkey in projects:
                ps = [p for p in project_objs if p.key == pkey]
                if not ps:
                    logger.error('Could not get project %s, ignore it', pkey)
                else:
                    project_filter_list.append('Project = {}'.format(ps[0].name))
            filter_str = ' AND '.join(project_filter_list)

        return {'filters':
                    {'issue-related-events-section': filter_str
                     }
                }

    def _delete_webhook(self, url=None, id=None):
        """Delete webhook
        :param url:
        :param id:
        :return:
        """
        if url is None:
            url = self.webhook_url + '/' + str(id)
        return self._requests(url, 'delete')

    def _get_cluster_name(self):
        """
        :return:
        """
        return os.environ.get('AX_CLUSTER', UNKNOWN_CLUSTER)

    def _requests(self, url, method, data=None, headers=None, auth=None, raise_exception=True, timeout=30):
        """
        :param url:
        :param method:
        :param data:
        :param headers:
        :param auth:
        :param raise_exception:
        :param timeout:
        :return:
        """
        headers = {'Content-Type': 'application/json'} if headers is None else headers
        auth = self.basic_auth if auth is None else auth
        try:
            response = requests.request(method, url, data=json.dumps(data), headers=headers, auth=auth, timeout=timeout)
        except requests.exceptions.RequestException as exc:
            logger.error('Unexpected exception occurred during request: %s', exc)
            raise
        logger.debug('Response status: %s (%s %s)', response.status_code, response.request.method, response.url)
        # Raise exception if status code indicates a failure
        if response.status_code >= 400:
            logger.error('Request failed (status: %s, reason: %s)', response.status_code, response.text)
        if raise_exception:
            response.raise_for_status()
        return response
コード例 #6
0
class Jira(object):
    """Python-Jira API class"""
    def __init__(self, args):
        """Init"""
        self.jira_server = 'https://company.jira.net'
        self.jira = JIRA('%s' % self.jira_server,
                         basic_auth=(args.jira_user, args.jira_pswd))

        # Arguments
        self.release_name = args.release_name
        self.release_date = args.release_date
        self.project = args.project
        self.status = args.status
        self.fix_version = args.fix_version
        self.tkts_resolved = args.tkts_resolved
        self.verbose = args.verbose

        self.issues = []

    def search_issues(self):
        """Return issues searched with JQL"""
        search_str = 'project = %s AND status = %s AND fixVersion = %s order by lastViewed DESC' % \
                     (self.project, self.status, self.fix_version)

        return self.jira.search_issues(search_str)

    def is_version_exist(self):
        """Check if version exist"""
        # New version is in the last element of the list
        version = self.jira.project_versions(self.project)[-1]

        if str(version) == self.release_name:
            return True
        else:
            return False

    def create_version(self):
        """Create Jira release version"""
        descrp = []

        # Adding tickets summary to release description
        for tkt in self.issues:
            descrp.append((tkt.fields.summary).encode('UTF8'))

        descrp = '. '.join(descrp)

        self.jira.create_version(self.release_name,\
                                 self.project,\
                                 description=descrp,\
                                 releaseDate=self.release_date,\
                                 startDate=None,\
                                 archived=False,\
                                 released=True)

    def tickets_resolved_log(self):
        """Create a file with resolved tickets summary"""
        version = self.jira.project_versions(self.project)[-1]
        release_url = '%s/projects/%s/versions/%s/tab/release-report-all-issues/' % (
            self.jira_server, self.project, str(version.id))

        hdr_str = "======================================================\n" \
                  "The new Fixes or Features added in this release are:\n" \
                  "\n" \
                  "%s\n\n" % release_url

        tail_str = "======================================================"

        with open(self.tkts_resolved, "a") as tkts_file:
            tkts_file.write(hdr_str)
            for tkt in self.issues:
                tkts_file.write("%s: %s \n" % (tkt.key, tkt.fields.summary))
            tkts_file.write(tail_str)

    def update_issue_fix_version(self):
        """ Update fixVersion of tickets"""
        fixVersion = []

        fixVersion.append({'name': self.release_name})
        for tkt in self.issues:
            tkt.update(fields={'fixVersions': fixVersion})

    def create_release(self):
        """ Workflow to create release:
            1. Search tickets for release
            2. Create Jira release version
            3. Update fixVersion of tickets
            4. Parse tickets resolved to file for release announcment
        """
        self.issues = self.search_issues()

        version_exist = self.is_version_exist()
        if version_exist == False:
            self.create_version()

        self.update_issue_fix_version()

        self.tickets_resolved_log()

    def command(self, args):
        """Commands for Jira release"""
        if 'create_release' in args.command:
            self.create_release()

        elif 'close_release' in args.command:
            self.close_release()
コード例 #7
0
props = load_properties('gradle.properties')
release_version = props['versionMajor'] + '.' + props['versionMinor'] + '.' + props['versionPatch']

for project in projects:
    version_exists = False
    versions = jira.project_versions(project)
    for version in versions:
        if version.name == release_version:
            version_exists = True
            break

    if(version_exists):
        print('version ' + release_version + ' in project ' + project + ' exists - dont create one\n')
    else:
        print('version ' + release_version + ' in project ' + project + ' not found - creating it!\n')
        version = jira.create_version(release_version, project_version)

issues = []
added = []
bugs = []

issues = scan_for_tickets()
for issueCode in issues:
    try:
        issue = jira.issue(issueCode)
    except JIRAError as e:
        print(issueCode + "not found")
    set_fixVersions(issue, version)
    if issue.fields.issuetype.name in bugTypes:
        bugs.append(issue)
    elif issue.fields.issuetype.name in ignoredTypes:
コード例 #8
0
def main():

    statusDict = {
        'integration': {
            'transitionId': 131,
            'status': 'MERGED'
        },
        'uat': {
            'transitionId': 151,
            'status': 'APPROVED'
        }
    }

    # Determine which product is to be deployed
    product = input(
        'Is this a \"VICE\", \"Workflow\", or \"Dispatch\" deployment?\n')

    while product.lower() not in [
            'vice', 'dispatch', 'dis', 'vc', 'workflow', 'wrk'
    ]:
        try:
            product = input('Invalid selection. Try again...\n')
        except:
            print('Ya blew it...')
            sys.exit(1)

    # Determine to which environment changes are to be deployed
    environment = input('Is this an \"integration\" or \"UAT\" deployment?\n')

    while environment.lower() not in ['integration', 'uat']:
        try:
            environment = input('Invalid selection. Try again...\n')
        except:
            print('Ya blew it...')
            sys.exit(1)

    # Assign appropriate values to variables based on selections
    if product.lower() in ['vice', 'vc']:
        project = 'VC'
    elif product.lower() in ['dispatch', 'dis']:
        project = 'DIS'
    elif product.lower() in ['workflow', 'wrk']:
        project = 'WRK'
    # project = 'SBX'

    if environment.lower() == 'integration':
        statusInfo = statusDict['integration']
    else:
        statusInfo = statusDict['uat']

    # Authenticate
    options = {'server': 'https://snapsheettech.atlassian.net/'}
    jira = JIRA(options, basic_auth=('EMAIL HERE', 'API KEY HERE'))

    # Create new version in the appropriate project. Use existing version if matching version already exists.
    version = input('Please provide a name for the Fix Version: ')
    try:
        newVersion = jira.create_version(version, project)
    except:
        projectVersions = jira.project_versions(project)
        for i in range(len(projectVersions)):
            if projectVersions[i].name == version:
                newVersion = projectVersions[i]
        pass

    # Transition issues and add new Fix Version
    print('\nWorking... This may take some time.\n')

    # Issue queries are broken up into 100 issue chunks due to limitations of the Jira API
    block_num = 0
    block_size = 100

    while True:
        start_idx = block_num * block_size
        issues = jira.search_issues(
            'project = %s AND status = %s order by created DESC' %
            (project, statusInfo['status']),
            startAt=start_idx,
            maxResults=block_size)
        if len(issues) == 0:
            break
        block_num += 1

        for issue in issues:
            fixVersions = []
            for version in issue.fields.fixVersions:
                fixVersions.append({'name': version.name})
            fixVersions.append({'name': newVersion.name})
            jira.transition_issue(issue.id,
                                  statusInfo['transitionId'],
                                  fields={'fixVersions': fixVersions},
                                  comment='Issue transitioned via automation.')

    print('All done.\n')
コード例 #9
0
ファイル: vira_api.py プロジェクト: todmorrison/vira
class ViraAPI():
    '''
    This class gets imported by __init__.py
    '''
    def __init__(self):
        '''
        Initialize vira
        '''

        # Load user-defined config files
        file_servers = vim.eval('g:vira_config_file_servers')
        file_projects = vim.eval('g:vira_config_file_projects')
        try:
            self.vira_servers = load_config(file_servers)
            self.vira_projects = load_config(file_projects)
        except:
            print(f'Could not load {file_servers} or {file_projects}')

        self.userconfig_filter_default = {
            'assignee': '',
            'component': '',
            'fixVersion': '',
            'issuetype': '',
            'priority': '',
            'project': '',
            'reporter': '',
            'status': '',
            "'Epic Link'": '',
            'statusCategory': ['To Do', 'In Progress'],
            'text': ''
        }
        self.reset_filters()

        self.userconfig_newissue = {
            'assignee': '',
            'component': '',
            'fixVersion': '',
            'issuetype': 'Bug',
            'priority': '',
            'epics': '',
            'status': '',
        }

        self.users = set()
        self.versions = set()
        self.servers = set()
        self.users_type = ''
        self.async_count = 0

        self.versions_hide(True)

    def _async(self, func):
        try:
            func()
        except:
            pass

    def _async_vim(self):
        #  TODO: VIRA-247 [210223] - Clean-up vim variables in python _async
        try:
            if len(vim.eval('s:versions')) == 0:
                vim.command('let s:projects = s:projects[1:]')
                if len(vim.eval('s:projects')) == 0:
                    #  TODO: VIRA-247 [210223] - Check for new projects and versions and start PRIORITY ranking for updates
                    vim.command('let s:vira_async_timer = g:vira_async_timer')
                    self.get_projects()
                self.get_versions()
            else:
                #  self.version_percent(str(vim.eval('s:projects[0]')), str(vim.eval('s:versions[0]')))
                vim.command('let s:versions = s:versions[1:]')

            if self.async_count == 0 and vim.eval(
                    's:vira_async_timer') == 10000:
                self.users = self.get_users()
                self.async_count = 1000
            self.async_count -= 1
        except:
            pass

    def create_issue(self, input_stripped):
        '''
        Create new issue in jira
        '''

        section = {
            'summary':
            parse_prompt_text(input_stripped, '*Summary*', 'Description'),
            'description':
            parse_prompt_text(input_stripped, 'Description', '*Project*'),
            'project':
            parse_prompt_text(input_stripped, '*Project*', '*IssueType*'),
            'issuetype':
            parse_prompt_text(input_stripped, '*IssueType*', 'Status'),
            'status':
            parse_prompt_text(input_stripped, 'Status', 'Priority'),
            'priority':
            parse_prompt_text(input_stripped, 'Priority', 'Component'),
            'components':
            parse_prompt_text(input_stripped, 'Component', 'Version'),
            'fixVersions':
            parse_prompt_text(input_stripped, 'Version', 'Assignee'),
            'assignee':
            parse_prompt_text(input_stripped, 'Assignee'),
        }

        # Check if required fields was entered by user
        if section['summary'] == '' or section['project'] == '' or section[
                'issuetype'] == '':
            return

        issue_kwargs = {
            'project': section['project'],
            'summary': section['summary'],
            'description': section['description'],
            'issuetype': {
                'name': section['issuetype']
            },
            'priority': {
                'name': section['priority']
            },
            'components': [{
                'name': section['components']
            }],
            'fixVersions': [{
                'name': section['fixVersions']
            }],
            'assignee': {
                'name': section['assignee']
            },
        }

        # Jira API doesn't accept empty fields for certain keys
        for key in issue_kwargs.copy().keys():
            if section[key] == '':
                issue_kwargs.pop(key)

        # Create issue and transition
        issue_key = self.jira.create_issue(**issue_kwargs)
        if section['status'] != '':
            self.jira.transition_issue(issue_key, section['status'])

        jira_server = vim.eval('g:vira_serv')
        print(f'Added {jira_server}/browse/{issue_key}')

    def add_worklog(self, issue, timeSpentSeconds, comment):
        '''
        Calculate the offset for the start time of the time tracking
        '''

        earlier = datetime.now() - datetime.timedelta(seconds=timeSpentSeconds)

        self.jira.add_worklog(issue=issue,
                              timeSpentSeconds=timeSpentSeconds,
                              comment=comment,
                              started=earlier)

    def connect(self, server):
        '''
        Connect to Jira server with supplied auth details
        '''

        self.users = set()
        self.versions = set()
        self.users_type = ''

        try:
            # Specify whether the server's TLS certificate needs to be verified
            if self.vira_servers[server].get('skip_cert_verify'):
                urllib3.disable_warnings(
                    urllib3.exceptions.InsecureRequestWarning)
                cert_verify = False
            else:
                cert_verify = True

            # Get auth for current server
            username = self.vira_servers[server].get('username')
            password_cmd = self.vira_servers[server].get('password_cmd')
            if password_cmd:
                password = run_command(password_cmd)['stdout'].strip().split(
                    '\n')[0]
            else:
                password = self.vira_servers[server]['password']
        except:
            cert_verify = True
            server = vim.eval('input("server: ")')
            vim.command('let g:vira_serv = "' + server + '"')
            username = vim.eval('input("username: "******"password: "******"' + server + '"')

            # Authorize
            self.jira = JIRA(options={
                'server': server,
                'verify': cert_verify,
            },
                             basic_auth=(username, password),
                             timeout=2,
                             async_=True,
                             max_retries=2)

            # Initial list updates
            self.users = self.get_users()
            self.get_projects()
            self.get_versions()

            vim.command('echo "Connection to jira server was successful"')
        except JIRAError as e:
            if 'CAPTCHA' in str(e):
                vim.command(
                    'echo "Could not log into jira! Check authentication details and log in from web browser to enter mandatory CAPTCHA."'
                )
            else:
                #  vim.command('echo "' + str(e) + '"')
                vim.command('let g:vira_serv = ""')
                #  raise e
        except:
            vim.command('let g:vira_serv = ""')
            vim.command(
                'echo "Could not log into jira! See the README for vira_server.json information"'
            )

    def filter_str(self, filterType):
        '''
        Build a filter string to add to a JQL query
        The string will look similar to one of these:
            AND status in ('In Progress')
            AND status in ('In Progress', 'To Do')
        '''

        if self.userconfig_filter.get(filterType, '') == '':
            return

        selection = str(
            self.userconfig_filter[filterType]).strip('[]') if type(
                self.userconfig_filter[filterType]
            ) == list else self.userconfig_filter[filterType] if type(
                self.userconfig_filter[filterType]
            ) == tuple else "'" + self.userconfig_filter[filterType] + "'"

        return str(f"{filterType} in ({selection})").replace(
            "'None'", "Null").replace("'Unassigned'", "Null").replace(
                "'currentUser'", "currentUser()").replace(
                    "'currentUser()'", "currentUser()").replace(
                        "'currentuser'", "currentUser()").replace(
                            "'currentuser()'",
                            "currentUser()").replace("'null'", "Null").replace(
                                f"text in ({selection})",
                                f"text ~ {selection}")

    def get_assign_issue(self):
        '''
        Menu to select users
        '''

        self.print_users()

    def get_assignees(self):
        '''
        Get my issues with JQL
        '''

        self.print_users()

    def get_comments(self, issue):
        '''
        Get all the comments for an issue
        '''

        # Get the issue requested
        issues = self.jira.search_issues('issue = "' + issue.key + '"',
                                         fields='summary,comment',
                                         json_result='True')

        # Loop through all of the comments
        comments = ''
        for comment in issues["issues"][0]["fields"]["comment"]["comments"]:
            comments += (f"{comment['author']['displayName']}" + ' | ',
                         f"{comment['updated'][0:10]}" + ' @ ',
                         f"{comment['updated'][11:16]}" + ' | ',
                         f"{comment['body']} + '\n'")

        return comments

    def get_components(self):
        '''
        Build a vim pop-up menu for a list of components
        '''

        for component in self.jira.project_components(
                self.userconfig_filter['project']):
            print(component.name)
        print('None')

    def get_component(self):
        '''
        Build a vim pop-up menu for a list of components
        '''

        self.get_components()

    def get_epic(self):
        self.get_epics()

    def get_epics(self):
        '''
        Get my issues with JQL
        '''
        hold = dict(self.userconfig_filter)
        project = self.userconfig_filter['project']
        self.reset_filters()
        self.userconfig_filter["issuetype"] = "Epic"
        self.userconfig_filter["project"] = project
        self.get_issues()
        print('None')
        self.userconfig_filter = hold

    def get_issue(self, issue):
        '''
        Get single issue by issue id
        '''

        return self.jira.issue(issue)

    def get_issues(self):
        '''
        Get my issues with JQL
        '''

        issues = []
        key_length = 0
        summary_length = 0
        issuetype_length = 0
        status_length = 4
        user_length = 0

        for issue in self.query_issues():
            fields = issue['fields']

            user = str(fields['assignee']['displayName']) if type(
                fields['assignee']) == dict else 'Unassigned'
            user_length = len(user) if len(user) > user_length else user_length

            key_length = len(
                issue['key']) if len(issue['key']) > key_length else key_length

            summary = fields['summary']
            summary_length = len(
                summary) if len(summary) > summary_length else summary_length

            issuetype = fields['issuetype']['name']
            issuetype_length = len(issuetype) if len(
                issuetype) > issuetype_length else issuetype_length

            status = fields['status']['name']
            status_length = len(
                status) if len(status) > status_length else status_length

            issues.append([
                issue['key'], fields['summary'], fields['issuetype']['name'],
                fields['status']['name'], user
            ])

        # Add min/max limits on summary length
        columns = vim.eval("&columns")
        min_summary_length = 25
        max_summary_length = int(
            columns) - key_length - issuetype_length - status_length - 28
        summary_length = min_summary_length if max_summary_length < min_summary_length else max_summary_length if summary_length > max_summary_length else summary_length

        for issue in issues:
            print(('{: <' + str(key_length) + '}').format(issue[0]) + " │ " +
                  ('{: <' + str(summary_length) +
                   '}').format(issue[1][:summary_length]) + "  │ " +
                  ('{: <' + str(issuetype_length) + '}').format(issue[2]) +
                  " │ " +
                  ('{: <' + str(status_length) + '}').format(issue[3]) +
                  ' │ ' + issue[4])

    def get_issuetypes(self):
        '''
        Get my issues with JQL
        '''

        for issuetype in self.jira.issue_types():
            print(issuetype)

    def get_issuetype(self):
        '''
        Get my issues with JQL
        '''

        for issuetype in self.jira.issue_types():
            print(issuetype)

    def get_priorities(self):
        '''
        Get my issues with JQL
        '''

        for priority in self.jira.priorities():
            print(priority)

    def print_projects(self):
        '''
        Build a vim pop-up menu for a list of projects
        '''

        all_projects = self.get_projects()
        batch_size = 10
        project_batches = [
            all_projects[i:i + batch_size]
            for i in range(0, len(all_projects), batch_size)
        ]

        for batch in project_batches:
            projects = self.jira.createmeta(projectKeys=','.join(batch),
                                            expand='projects')['projects']
            [print(p['key'] + ' ~ ' + p['name']) for p in projects]

    def get_projects(self):
        '''
        Build a vim pop-up menu for a list of projects
        '''

        # Project filter for version list
        self.projects = []
        for project in self.jira.projects():
            self.projects.append(str(project))
        vim.command('let s:projects = ' + str(self.projects))

        return self.projects

    def get_priority(self):
        '''
        Build a vim pop-up menu for a list of projects
        '''

        self.get_priorities()

    def get_prompt_text(self, prompt_type, comment_id=None):
        '''
        Get prompt text used for inputting text into jira
        '''

        self.prompt_type = prompt_type

        # Edit filters
        if prompt_type == 'edit_filter':
            self.prompt_text_commented = '\n# Edit all filters in JSON format'
            self.prompt_text = json.dumps(
                self.userconfig_filter,
                indent=True) + self.prompt_text_commented
            return self.prompt_text

        # Edit summary
        active_issue = vim.eval("g:vira_active_issue")
        if prompt_type == 'summary':
            self.prompt_text_commented = '\n# Edit issue summary'
            summary = self.jira.search_issues(
                'issue = "' + active_issue + '"',
                fields=','.join(['summary']),
                json_result='True')['issues'][0]['fields']['summary']
            self.prompt_text = summary + self.prompt_text_commented
            return self.prompt_text

        # Edit description
        if prompt_type == 'description':
            self.prompt_text_commented = '\n# Edit issue description'
            description = self.jira.search_issues(
                'issue = "' + active_issue + '"',
                fields=','.join(['description']),
                json_result='True')['issues'][0]['fields'].get('description')
            if description:
                description = description.replace('\r\n', '\n')
            else:
                description = ''
            self.prompt_text = description + self.prompt_text_commented
            return self.prompt_text

        self.prompt_text_commented = '''
# ---------------------------------
# Please enter text above this line
# An empty message will abort the operation.
#
# Below is a list of acceptable values for each input field.
#
# Users:'''
        for user in self.users:
            user = user.split(' ~ ')
            name = user[0]
            id = user[1]
            if self.users_type == 'accountId':
                self.prompt_text_commented += f'''
# [{name}|~accountid:{id}]'''
            else:
                self.prompt_text_commented += f'''
# [~{id}]'''

        # Add comment
        if self.prompt_type == 'add_comment':
            self.prompt_text = self.prompt_text_commented
            return self.prompt_text

        # Edit comment
        if self.prompt_type == 'edit_comment':
            self.active_comment = self.jira.comment(active_issue, comment_id)
            self.prompt_text = self.active_comment.body + self.prompt_text_commented
            return self.prompt_text

        statuses = [x.name for x in self.jira.statuses()]
        issuetypes = [x.name for x in self.jira.issue_types()]
        priorities = [x.name for x in self.jira.priorities()]
        components = [
            x.name for x in self.jira.project_components(
                self.userconfig_filter['project'])
        ] if self.userconfig_filter['project'] != '' else ''
        versions = [
            x.name for x in self.jira.project_versions(
                self.userconfig_filter['project'])
        ] if self.userconfig_filter['project'] != '' else ''
        projects = [x.key for x in self.jira.projects()]

        # Extra info for prompt_type == 'issue'
        self.prompt_text_commented += f'''
#
# Projects: {projects}
# IssueTypes: {issuetypes}
# Statuses: {statuses}
# Priorities: {priorities}
# Components in {self.userconfig_filter["project"]} Project: {components}
# Versions in {self.userconfig_filter["project"]} Project: {versions}'''

        self.prompt_text = f'''[*Summary*]
[Description]

[*Project*] {self.userconfig_filter["project"]}
[*IssueType*] {self.userconfig_newissue["issuetype"]}
[Status] {self.userconfig_newissue["status"]}
[Priority] {self.userconfig_newissue["priority"]}
[Component] {self.userconfig_newissue["component"]}
[Version] {self.userconfig_newissue["fixVersion"]}
[Assignee] {self.userconfig_newissue["assignee"]}
{self.prompt_text_commented}'''
        return self.prompt_text

    def format_date(self, date):
        time = datetime.now().strptime(date,
                                       '%Y-%m-%dT%H:%M:%S.%f%z').astimezone()
        return str(time)[0:10] + ' ' + str(time)[11:16]

    def get_report(self):
        '''
        Print a report for the given issue
        '''

        for customfield in self.jira.fields():
            if customfield['name'] == 'Epic Link':
                epicID = customfield['id']

        # Get passed issue content
        active_issue = vim.eval("g:vira_active_issue")
        issues = self.jira.search_issues(
            'issue = "' + active_issue + '"',
            fields=','.join([
                'project', 'summary', 'comment', 'component', 'description',
                'issuetype', 'priority', 'status', 'created', 'updated',
                'assignee', 'reporter', 'fixVersion', 'customfield_10106',
                'labels', epicID
            ]),
            json_result='True')
        issue = issues['issues'][0]['fields']

        # Prepare report data
        open_fold = '{{{'
        close_fold = '}}}'
        summary = issue['summary']
        story_points = str(issue.get('customfield_10106', ''))
        created = self.format_date(issue['created'])
        updated = self.format_date(issue['updated'])
        issuetype = issue['issuetype']['name']
        status = issue['status']['name']
        priority = issue['priority']['name']
        assignee = issue['assignee']['displayName'] if type(
            issue['assignee']) == dict else 'Unassigned'
        reporter = issue['reporter']['displayName']
        component = ', '.join([c['name'] for c in issue['components']])
        version = ', '.join([v['name'] for v in issue['fixVersions']])
        epics = str(issue.get(epicID))
        vim.command(f'let s:vira_epic_field = "{epicID}"')
        description = str(issue.get('description'))

        # Version percent for single version attacted
        #  if len(issue['fixVersions']) == 1 and version != '':
        #  version += ' | ' + self.version_percent(
        #  str(issue['project']['key']), version) + '%'

        comments = ''
        idx = 0
        for idx, comment in enumerate((issue['comment']['comments'])):
            comments += ''.join([
                comment['author']['displayName'] + ' @ ' +
                self.format_date(comment['updated']) + ' {{' + '{2\n' +
                comment['body'] + '\n}}}\n'
            ])
        old_count = idx - 3
        old_comment = 'Comment' if old_count == 1 else 'Comments'
        comments = ''.join(
            [str(old_count) + ' Older ' + old_comment +
             ' {{{1\n']) + comments if old_count >= 1 else comments
        comments = comments.replace('}}}', '}}}}}}', idx - 3)
        comments = comments.replace('}}}}}}', '}}}', idx - 4)

        # Find the length of the longest word [-1]
        words = [
            created, updated, issuetype, status, story_points, priority,
            component, version, assignee, reporter, epics
        ]
        wordslength = sorted(words, key=len)[-1]
        s = '─'
        dashlength = s.join([char * len(wordslength) for char in s])

        active_issue_spacing = int((16 + len(dashlength)) / 2 -
                                   len(active_issue) / 2)
        active_issue_spaces = ' '.join(
            [char * (active_issue_spacing) for char in ' '])
        active_issue_space = ' '.join([
            char * ((len(active_issue) + len(dashlength)) % 2) for char in ' '
        ])

        created_spaces = ' '.join(
            [char * (len(dashlength) - len(created)) for char in ' '])
        updated_spaces = ' '.join(
            [char * (len(dashlength) - len(updated)) for char in ' '])
        task_type_spaces = ' '.join(
            [char * (len(dashlength) - len(issuetype)) for char in ' '])
        status_spaces = ' '.join(
            [char * (len(dashlength) - len(status)) for char in ' '])
        story_points_spaces = ''.join(
            [char * (len(dashlength) - len(story_points)) for char in ' '])
        priority_spaces = ''.join(
            [char * (len(dashlength) - len(priority)) for char in ' '])
        component_spaces = ''.join(
            [char * (len(dashlength) - len(component)) for char in ' '])
        version_spaces = ''.join(
            [char * (len(dashlength) - len(version)) for char in ' '])
        assignee_spaces = ''.join(
            [char * (len(dashlength) - len(assignee)) for char in ' '])
        reporter_spaces = ''.join(
            [char * (len(dashlength) - len(reporter)) for char in ' '])
        epics_spaces = ''.join(
            [char * (len(dashlength) - len(epics)) for char in ' '])

        # Create report template and fill with data
        report = '''┌────────────────{dashlength}─┐
│{active_issue_spaces}{active_issue}{active_issue_spaces}{active_issue_space} │
├──────────────┬─{dashlength}─┤
│      Created │ {created}{created_spaces} │
│      Updated │ {updated}{updated_spaces} │
│         Type │ {issuetype}{task_type_spaces} │
│       Status │ {status}{status_spaces} │
│ Story Points │ {story_points}{story_points_spaces} │
│     Priority │ {priority}{priority_spaces} │
│    Epic Link │ {epics}{epics_spaces} │
│ Component(s) │ {component}{component_spaces} │
│   Version(s) │ {version}{version_spaces} │
│     Assignee │ {assignee}{assignee_spaces} │
│     Reporter │ {reporter}{reporter_spaces} │
└──────────────┴─{dashlength}─┘
┌──────────────┐
│    Summary   │
└──────────────┘
{summary}

┌──────────────┐
│  Description │
└──────────────┘
{description}

┌──────────────┐
│   Comments   │
└──────────────┘
{comments}'''

        self.set_report_lines(report, description, issue)

        self.prompt_text = self.report_users(report.format(**locals()))
        return self.prompt_text

    def report_users(self, report):
        '''
        Replace report accountid with names
        '''

        for user in self.users:
            user = user.split(' ~ ')
            if user[0] != "Unassigned":
                report = report.replace('accountid:',
                                        '').replace('[~' + user[1] + ']',
                                                    '[~' + user[0] + ']')

        return report

    def get_reporters(self):
        '''
        Get my issues with JQL
        '''

        self.print_users()

    def get_servers(self):
        '''
        Get list of servers
        '''

        try:
            for server in self.vira_servers.keys():
                print(server)
            print('Null')
        except:
            self.connect('')

    def get_statuses(self):
        '''
        Get my issues with JQL
        '''

        statuses = []
        for status in self.jira.statuses():
            if str(status) not in statuses:
                statuses.append(str(status))
                print(str(status))

    def get_set_status(self):
        '''
        Get my issues with JQL
        '''

        self.get_statuses()

    def get_version(self):
        '''
        Get my issues with JQL
        '''

        self.print_versions()

    def new_component(self, name, project):
        '''
        New component added to project
        '''

        self.jira.create_component(name=name,
                                   project=project,
                                   description=name)

    def new_version(self, name, project, description):
        '''
        Get my issues with JQL
        '''

        self.jira.create_version(name=name,
                                 project=project,
                                 description=description)

    def print_users(self):
        '''
        Print users
        '''

        print(self.get_current_user() + ' ~ currentUser')
        for user in self.users:
            print(user)
        print('Unassigned')

    def get_users(self):
        '''
        Get my issues with JQL
        '''

        query = 'ORDER BY updated DESC'
        issues = self.jira.search_issues(query,
                                         fields='assignee, reporter',
                                         json_result='True',
                                         maxResults=-1)

        # Determine cloud/server jira
        self.users_type = 'accountId' if issues['issues'][0]['fields'][
            'reporter'].get('accountId') else 'name'

        for issue in issues['issues']:
            user = str(issue['fields']['reporter']['displayName']
                       ) + ' ~ ' + issue['fields']['reporter'][self.users_type]
            self.users.add(user)
            if type(issue['fields']['assignee']) == dict:
                user = str(
                    issue['fields']['assignee']['displayName']
                ) + ' ~ ' + issue['fields']['assignee'][self.users_type]
            self.users.add(user)

        return sorted(self.users)

    def get_current_user(self):
        query = 'reporter = currentUser() or assignee = currentUser()'
        issues = self.jira.search_issues(query,
                                         fields='assignee, reporter',
                                         json_result='True',
                                         maxResults=-1)

        issue = issues['issues'][0]['fields']
        return str(issue['assignee'][self.users_type] if type(
            issue['assignee']) == dict else issue['reporter'][self.users_type]
                   if type(issue['reporter']) == dict else 'Unassigned')

    def print_versions(self):
        '''
        Print version list with project filters
        '''
        try:
            versions = sorted(self.versions)
            wordslength = sorted(versions, key=len)[-1]
            s = ' '
            dashlength = s.join([char * len(wordslength) for char in s])
            for version in versions:
                print(
                    version.split('|')[0] + ''.join([
                        char * (len(dashlength) - len(version)) for char in ' '
                    ]) + '   ' + version.split('|')[1] + ' ' +
                    version.split('|')[2])
        except:
            pass
        print('None')

    def version_percent(self, project, fixVersion):
        project = str(project)
        fixVersion = str(fixVersion)
        if str(project) != '[]' and str(project) != '' and str(
                fixVersion) != '[]' and str(fixVersion) != '':
            query = 'fixVersion = ' + fixVersion + ' AND project = "' + project + '"'
            issues = self.jira.search_issues(query,
                                             fields='fixVersion',
                                             json_result='True',
                                             maxResults=1)

            try:
                issue = issues['issues'][0]['fields']['fixVersions'][0]
                idx = issue['id']

                total = self.jira.version_count_related_issues(
                    idx)['issuesFixedCount']
                pending = self.jira.version_count_unresolved_issues(idx)
                fixed = total - pending
                percent = str(round(fixed / total *
                                    100, 1)) if total != 0 else 1
                space = ''.join([char * (5 - len(percent)) for char in ' '])

                name = fixVersion
                try:
                    description = issue['description']
                except:
                    description = 'None'
                    pass
            except:
                total = 0
                pending = 0
                fixed = total - pending
                percent = "0"
                space = ''.join([char * (5 - len(percent)) for char in ' '])
                name = fixVersion
                description = ''
                pass

            version = str(
                str(name) + ' ~ ' + str(description) + '|' + str(fixed) + '/' +
                str(total) + space + '|' + str(percent) + '%')

            self.versions_hide = vim.eval('g:vira_version_hide')
            if fixed != total or total == 0 or not int(
                    self.versions_hide) == 1:
                self.versions.add(
                    str(project) + ' ~ ' + str(version.replace('\'', '')))

        else:
            percent = 0

        return percent

    def get_versions(self):
        '''
        Build a vim pop-up menu for a list of versions with project filters
        '''

        # Loop through each project and all versions within
        try:
            for v in reversed(
                    self.jira.project_versions(vim.eval('s:projects[0]'))):
                vim.command('let s:versions = add(s:versions,\"' + str(v) +
                            '\")')
        except:
            vim.command('let s:versions = []')

    def load_project_config(self, repo):
        '''
        Load project configuration for the current git repo
        The current repo can either be determined by current files path
        or by the user setting g:vira_repo (part of :ViraLoadProject)

        For example, an entry in projects.yaml may be:

        vira:
          server: https://jira.tgall.ca
          project_name: VIRA
        '''

        # Only proceed if projects file parsed successfully
        if not getattr(self, 'vira_projects', None):
            return

        # If current repo/folder doesn't exist, use __default__ project config if it exists
        if repo == '':
            repo = run_command(
                'git rev-parse --show-toplevel')['stdout'].strip()
            if not self.vira_projects.get(repo):
                repo = repo.split('/')[-1]
            if not self.vira_projects.get(repo):
                repo = run_command('pwd')['stdout'].strip()
            if not self.vira_projects.get(repo):
                repo = repo.split('/')[-1]
            if not self.vira_projects.get(repo):
                repo = '__default__'
            if not self.vira_projects.get('__default__'):
                return

        # Set server
        server = self.vira_projects.get(repo, {}).get('server')
        if server:
            vim.command(f'let g:vira_serv = "{server}"')

        # Set user-defined filters for current project
        for key in self.userconfig_filter.keys():
            value = self.vira_projects.get(repo, {}).get('filter', {}).get(key)
            if value:
                self.userconfig_filter[key] = value

        # Set user-defined new-issue defaults for current project
        for key in self.userconfig_newissue.keys():
            value = self.vira_projects.get(repo, {}).get('newissue',
                                                         {}).get(key)
            if value:
                self.userconfig_newissue[key] = value

        # Set user-defined issue sort options
        sort_order = self.vira_projects.get(repo,
                                            {}).get('issuesort',
                                                    'updated DESC')
        self.userconfig_issuesort = ', '.join(sort_order) if type(
            sort_order) == list else sort_order

    def query_issues(self):
        '''
        Query issues based on current filters
        '''

        q = []
        for filterType in self.userconfig_filter.keys():
            filter_str = self.filter_str(filterType)
            if filter_str:
                q.append(filter_str)

        query = ' AND '.join(q) + ' ORDER BY ' + self.userconfig_issuesort
        issues = self.jira.search_issues(
            query,
            fields='summary,comment,status,statusCategory,issuetype,assignee',
            json_result='True',
            maxResults=vim.eval('g:vira_issue_limit'))

        return issues['issues']

    def reset_filters(self):
        '''
        Reset filters to their default values
        '''

        self.userconfig_filter = dict(self.userconfig_filter_default)

    def set_report_lines(self, report, description, issue):
        '''
        Create dictionary for vira report that shows relationship
        between line numbers and fields to be edited
        '''

        writable_fields = {
            'Assignee': 'ViraSetAssignee',
            'Component': 'ViraSetComponent',
            'Priority': 'ViraSetPriority',
            'Epic Link': 'ViraSetEpic',
            'Status': 'ViraSetStatus',
            'Type': 'ViraSetType',
            'Version': 'ViraSetVersion',
        }

        self.report_lines = {}

        for idx, line in enumerate(report.split('\n')):
            for field, command in writable_fields.items():
                if field in line:
                    self.report_lines[idx + 1] = command
                    continue

        for x in range(16, 21):
            self.report_lines[x] = 'ViraEditSummary'

        description_len = description.count('\n') + 3
        for x in range(21, 23 + description_len):
            self.report_lines[x] = 'ViraEditDescription'

        offset = 2 if len(issue['comment']['comments']) > 4 else 1
        comment_line = 25 + description_len + offset
        for comment in issue['comment']['comments']:
            comment_len = comment['body'].count('\n') + 3
            for x in range(comment_line, comment_line + comment_len):
                self.report_lines[x] = 'ViraEditComment ' + comment['id']
            comment_line = comment_line + comment_len

    def set_prompt_text(self):
        '''
        Take the user prompt text and perform an action
        Usually, this involves writing to the jira server
        '''

        # User input
        issue = vim.eval('g:vira_active_issue')
        userinput = vim.eval('g:vira_input_text')
        input_stripped = userinput.replace(self.prompt_text_commented.strip(),
                                           '').strip()

        # Check if anything was actually entered by user
        if input_stripped == '' or userinput.strip() == self.prompt_text.strip(
        ):
            print("No vira actions performed")
            return

        if self.prompt_type == 'edit_filter':
            self.userconfig_filter = json.loads(input_stripped)
        elif self.prompt_type == 'add_comment':
            self.jira.add_comment(issue, input_stripped)
        elif self.prompt_type == 'edit_comment':
            self.active_comment.update(body=input_stripped)
        elif self.prompt_type == 'summary':
            self.jira.issue(issue).update(summary=input_stripped)
        elif self.prompt_type == 'description':
            self.jira.issue(issue).update(description=input_stripped)
        elif self.prompt_type == 'issue':
            self.create_issue(input_stripped)

    def versions_hide(self, state):
        '''
        Display and hide complete versions
        '''

        if state is True or 1 or 'ture' or 'True':
            self.version_hide = True
        elif state is False or 0 or 'false' or 'False':
            self.version_hide = False
        else:
            self.version_hide = not self.version_hide
コード例 #10
0
            if dba == "dba":
                dba_needed = True

            devgroup = app.upper() + "-developers"
            datetime = day + "T" + deploytime
            project = jira.project(app.upper())

            # Make sure Release Version exists in JIRA
            versions = jira.project_versions(project)
            print "Checking for the Version..."
            for v in versions:
                if v.name == version:
                    version_exists = True
            if not version_exists:
                print "The version %s does NOT exist.  Adding..." % version
                jira.create_version(version, app.upper())

            # Make sure Components exists in JIRA Project
            components = jira.project_components(project)
            print "Checking for Components..."
            for c in components:
                if c.name == "webapp":
                    webapp = True
                if c.name == "database":
                    database = True

            if not webapp:
                print "webapp component does not exist.  Adding..."
                jira.create_component("webapp", app.upper(), None, None,
                                      "UNASSIGNED", False)
            if not database:
コード例 #11
0
ファイル: jira.py プロジェクト: TangleInc/release-tool
class JiraAPI:
    """
    Jira client has no documentation, so if you need one, use one for REST API:
    https://developer.atlassian.com/cloud/jira/platform/rest/v3/
    """
    def __init__(self, settings: Settings):
        self._settings = settings
        self.transition = settings.jira.transition
        self.release_task = settings.jira.release_task
        self.release_task_name = self.release_task.name.format(
            version=settings.version, component=self.release_task.component)

        self._api = JIRA(
            {"server": settings.jira.connection.server},
            basic_auth=(settings.jira.connection.user,
                        settings.jira.connection.token),
        )

    def _create_version(self, project: Project):
        proposed_name = "Hotfix" if self._settings.version.minor > 0 else "Release"
        user_input = input(f"Input new Jira version name: [{proposed_name}]: ")
        name = user_input if user_input else proposed_name

        return self._api.create_version(name,
                                        project,
                                        startDate=_get_formatted_date())

    def _select_version(self, project: Project,
                        unreleased_versions) -> Optional[Version]:
        print("Jira versions:")
        print("1) Skip")
        print("2) Create new")
        print("or select existing one:")

        unreleased_versions = {
            idx: version
            for idx, version in enumerate(unreleased_versions, 3)
        }

        for idx, version in unreleased_versions.items():
            print(f"{idx}) {version.name}")

        user_input = 0
        valid_choices = list(range(1, len(unreleased_versions) + 3))

        while user_input not in valid_choices:
            try:
                user_input = int(
                    input(
                        "\nChoose which Jira version use for this release: "))
            except Exception:
                continue

        if user_input == 1:
            return None
        elif user_input == 2:
            return self._create_version(project)
        else:
            return unreleased_versions[user_input]

    def get_version(self) -> Optional[Version]:
        print_title(f"Searching for Jira release version")
        project = self._api.project(self.release_task.project)

        unreleased_versions = [
            v for v in self._api.project_versions(project) if not v.released
        ]

        return self._select_version(project, unreleased_versions)

    def _get_jira_release_unfinished_tasks(self, version: Version):
        """
        To check that all tasks in Jira release is finished
        select them using jql
        """
        final_statuses = '", "'.join(self.transition.child_final_statuses)
        types_to_skip = '", "'.join(self.transition.child_task_types_to_skip)

        return self._api.search_issues(
            f'project = "{self.release_task.project}"'
            f' AND fixVersion = "{version.name}"'
            f' AND fixVersion in unreleasedVersions("{self.release_task.project}")'
            f' AND status NOT IN ("{final_statuses}")'
            f' AND type NOT IN ("{types_to_skip}")')

    def _get_transition(self, issue, transition_name):
        transitions = [
            t for t in self._api.transitions(issue)
            if t["name"].lower() == transition_name.lower()
        ]
        if not transitions:
            return None

        return transitions[0]

    def release_version(self, release_task_key: str):
        print_title(
            f"Releasing Jira version of release task {release_task_key}")
        release_task = self._api.issue(release_task_key)

        for version in release_task.fields.fixVersions:
            version: Version
            print(f'Checking Jira release version: "{version.name}"...')

            if version.released:
                print_error("Version is already released")
                continue

            unfinished_tasks = self._get_jira_release_unfinished_tasks(version)
            if unfinished_tasks:
                tasks_str = ", ".join([i.key for i in unfinished_tasks])
                print_error(
                    f'Can\'t release Jira version: "{version.name}", it has unfinished tasks: {tasks_str}'
                )
                continue

            print("Jira version is safe to release, releasing...", end=" ")
            version.update(released=True, releaseDate=_get_formatted_date())
            print("Ok!")

    def _add_to_release_version(self, version: Version, release_task_key: str):
        issue = self._api.issue(release_task_key)
        issue.add_field_value("fixVersions", {"name": version.name})

    def make_links(self, version: Optional[Version], release_task_key,
                   related_keys):
        version_name = version.name if version else "-"
        print_title(f"Linking tasks found in release branch"
                    f" to release task ({release_task_key})"
                    f' and to Jira version "{version_name}"')
        if version:
            self._add_to_release_version(version, release_task_key)

        print(f"Linking {len(related_keys)} tasks:")
        partial_make_links = partial(self._make_links, version,
                                     release_task_key)
        with ThreadPool(5) as pool:
            pool.map(partial_make_links, related_keys)

    def _make_links(self, version: Optional[Version], release_task_key: str,
                    child_task_key: str):
        print(f"* {child_task_key}")
        self._api.create_issue_link(self.release_task.link_type,
                                    release_task_key, child_task_key)
        if version:
            self._add_to_release_version(version, child_task_key)

    def make_release_task(self):
        print_title("Creating Jira release task")
        extra_fields = {"components": [{"name": self.release_task.component}]}

        issue = self._api.create_issue(
            project=self.release_task.project,
            summary=self.release_task_name,
            issuetype={"name": self.release_task.type},
            **extra_fields,
        )
        print(f"Created Jira release task: {issue.key}")
        return issue.key

    def get_release_task(self):
        print_title("Searching for Jira release task")
        query = (f'project = "{self.release_task.project}"'
                 f' AND summary ~ "{self.release_task_name}"'
                 f' AND type = "{self.release_task.type}"')
        found_issues = self._api.search_issues(query)

        if not found_issues:
            print("Did not find existing release task")
            return self.make_release_task()

        if len(found_issues) > 1:
            issues_str = ", ".join([i.key for i in found_issues])
            print_error(
                f"Your release task has not unique name, fix it before using this functionality,"
                f" found issues: {issues_str}")
            exit(1)

        release_issue = found_issues[0]
        print(f"Found Jira release task: {release_issue.key}")
        return release_issue.key

    def mark_release_task_done(self, release_task_key):
        print_title(
            f'Transition release task "{release_task_key}" from "{self.transition.release_from_status}" to "{self.transition.release_to_status}"'
        )

        release_issue = self._api.issue(release_task_key)
        print_title(
            f'Current release task status is "{release_issue.fields.status}"')

        if (release_issue.fields.status.name.lower() !=
                self.transition.release_from_status.lower()):
            print_error(
                f'Release task "{release_task_key}" has inproper status')
            return

        transition = self._get_transition(release_issue,
                                          self.transition.release_to_status)

        if not transition:
            print_error(
                f'Release task "{release_task_key}" has no transition to "{self.transition.release_to_status}"'
            )
            return

        self._api.transition_issue(release_issue, transition["id"])

        print(
            f'Release task {release_issue.key} has been transited to status "{transition["name"]}"'
        )

    def mark_children_tasks_done(self, release_task_key):
        print_title(
            f'Transition children of "{release_task_key}" from "{self.transition.child_from_status}" to "{self.transition.child_to_status}"'
        )

        query = (f'issue in linkedIssues("{release_task_key}")'
                 f' AND status = "{self.transition.child_from_status}"')
        found_issues = self._api.search_issues(query)

        to_status = self.transition.child_to_status.lower()

        if not found_issues:
            print("Did not find any task for transition")
            return

        for issue in found_issues:
            transition = self._get_transition(issue, to_status)
            if not transition:
                print_error(
                    f'Issue "{issue.key}" does not have transition to status "{self.transition.child_to_status}"'
                )
                continue

            self._api.transition_issue(issue, transition["id"])
            print(
                f'Task {issue.key} has been transited to status "{transition["name"]}"'
            )
コード例 #12
0
ファイル: pxe-jira.py プロジェクト: sreeramk08/jenkins2.0
class Jira(object):
    """Python-Jira API class"""
    def __init__(self, args):
        """Init"""
        self.jira_server = 'https://company.jira.net'
        self.jira = JIRA('%s' % self.jira_server, basic_auth=(args.jira_user,args.jira_pswd))

        # Arguments
        self.release_name = args.release_name
        self.release_date = args.release_date
        self.project = args.project
        self.status = args.status
        self.fix_version = args.fix_version
        self.tkts_resolved = args.tkts_resolved
        self.verbose = args.verbose

        self.issues = []

    def search_issues(self):
        """Return issues searched with JQL"""
        search_str = 'project = %s AND status = %s AND fixVersion = %s order by lastViewed DESC' % \
                     (self.project, self.status, self.fix_version)

        return self.jira.search_issues(search_str)

    def is_version_exist(self):
        """Check if version exist"""
        # New version is in the last element of the list
        version = self.jira.project_versions(self.project)[-1]

        if str(version) == self.release_name:
            return True
        else:
            return False

    def create_version(self):
        """Create Jira release version"""
        descrp = []

        # Adding tickets summary to release description
        for tkt in self.issues:
            descrp.append((tkt.fields.summary).encode('UTF8'))

        descrp = '. '.join(descrp)

        self.jira.create_version(self.release_name,\
                                 self.project,\
                                 description=descrp,\
                                 releaseDate=self.release_date,\
                                 startDate=None,\
                                 archived=False,\
                                 released=True)

    def tickets_resolved_log(self):
        """Create a file with resolved tickets summary"""
        version = self.jira.project_versions(self.project)[-1]
        release_url = '%s/projects/%s/versions/%s/tab/release-report-all-issues/' % (self.jira_server, self.project, str(version.id))
        
        hdr_str = "======================================================\n" \
                  "The new Fixes or Features added in this release are:\n" \
                  "\n" \
                  "%s\n\n" % release_url 

        tail_str = "======================================================"

        with open(self.tkts_resolved, "a") as tkts_file:
            tkts_file.write(hdr_str)
            for tkt in self.issues:
                tkts_file.write("%s: %s \n" % (tkt.key, tkt.fields.summary))
            tkts_file.write(tail_str)

    def update_issue_fix_version(self):
        """ Update fixVersion of tickets"""
        fixVersion = []

        fixVersion.append({'name': self.release_name})
        for tkt in self.issues:
            tkt.update(fields={'fixVersions': fixVersion})

    def create_release(self):
        """ Workflow to create release:
            1. Search tickets for release
            2. Create Jira release version
            3. Update fixVersion of tickets
            4. Parse tickets resolved to file for release announcment
        """
        self.issues = self.search_issues()

        version_exist = self.is_version_exist()
        if version_exist == False:
            self.create_version()

        self.update_issue_fix_version()

        self.tickets_resolved_log()

    def command(self, args):
        """Commands for Jira release"""
        if 'create_release' in args.command:
            self.create_release()

        elif 'close_release' in args.command:
            self.close_release()
コード例 #13
0
from jira import JIRA

# You can either use your Jira username and password OR get API token from Jira to connect to
# your Jira instance. Getting API token is preferable, because you don't want to expose your plain text
# password in python code.
# Atlassian has instructions at https://confluence.atlassian.com/cloud/api-tokens-938839638.html
# to create api token.

options = {"server": "https://your-site.atlassian.net"}
jira = JIRA(options, basic_auth=("your-user-name", "api-token"))

LIST_OF_PROJECTS = ["PLATFORM", "PAYMENTS", "ENTERPRISE", "DATA"]

# Expand this list, if you want to create multiple fix versions at once.
# Make sure fixversion and releaseDates lists have same number of values
LIST_OF_FIXVERSIONS = ["2019-12-18 US Release"]
LIST_OF_RELEASEDATES = ["2019-12-18"]

# Loop thru project list and create fix versions for each project.
# Don't worry about creating duplicate fix version, JIRA will reject duplicate requests
for i in enumerate(LIST_OF_PROJECTS):
    for j in enumerate(LIST_OF_FIXVERSIONS):
        newversion = jira.create_version(
            name=LIST_OF_FIXVERSIONS[j],
            project=LIST_OF_PROJECTS[i],
            releaseDate=LIST_OF_RELEASEDATES[j],
        )
        print(LIST_OF_FIXVERSIONS[j] + " is created for project " +
              LIST_OF_PROJECTS[i])