Esempio n. 1
0
    def start(self):
        try:
            self.jira_api = JiraApi()

            # Check URL and user authentication
            if not self.jira_api.check_url_and_user():
                assert 'Check URL and User call failed!'

            # We want to run a specific filter in JIRA.
            # First give the filter Id and get the URL to run
            filter_url = self.jira_api.get_filter_for_id(config.JIRA_FILTER_ID)

            # Run the filter and get a list of issues to migrate
            issues_list = self.jira_api.get_filter(filter_url)

            # Setup progress bar
            total = len(issues_list['issues'])
            total_str = '/' + str(total)
            pbar_count = 0
            pbar = progressbar.ProgressBar(widgets=[
                progressbar.Bar(),
                progressbar.Counter(), total_str, ' ',
                progressbar.ETA(), ' ',
                progressbar.Timer()
            ],
                                           maxval=total).start()

            # Iterate over each issue and get details
            for issue in issues_list['issues']:

                key = issue['key']

                pbar_count += 1
                pbar.update(pbar_count)
                logger.info(
                    '[{key}] Processing: {pbar_count}{total_str}'.format(
                        key=key, pbar_count=pbar_count, total_str=total_str))

                status, issue_info = self.jira_api.get_issue_info(key)

                if not status:
                    logger.critical(
                        '[{key}] ERROR: Not able to get issue info'.format(
                            key=key))
                    continue

                self.jira_api.get_attachment(key, issue_info)

                # Now create this issue in JitBit
                self._migrate_to_jitbit(key, issue_info)

            pbar.finish

        except Exception as e:
            logger.critical(e.message)
            raise
Esempio n. 2
0
    def __init__(self):
        logger.info('Starting ProcessData ..')
        self.jitbit_api = JitbitApi()
        self.jira_api = JiraApi()

        # All assigned by is set to one user.
        self.default_assign_id = self.jitbit_api.get_user_id_by_email(
            config.JITBIT_DEFAULT_ASSIGN_EMAIL)

        self.start_time = time.time()
Esempio n. 3
0
class MetricsAggregator:
    def __init__(self):
        self.github = GithubApi()
        self.circleci = CircleciApi()
        self.jira = JiraApi()

    def print_lead_time(self, project, limit):
        lead_times = self.github.get_lead_time_array(project, limit)
        delta_list = list(map(lambda lead_time: lead_time["delta"],
                              lead_times))
        count = len(delta_list)
        if count == 0:
            print("No PRs found with current criteria.")
            return
        delta_list.sort()
        average = round(sum(delta_list) / count, 2)
        median = round(delta_list[int(count / 2)], 2)
        print(
            f"Lead time metrics (in hours) for the last {count} feature PRs in {project}: "
        )
        print(f"  Median: {median}")
        print(f"  Average: {average}")

    def print_deployment_frequency(self, max_age):
        # TODO update to releases/dev/day
        # TODO see how frequent deployments are on other repos (i.e. mob-api)
        releases_response = self.github.get_releases(project="mob-api")
        releases = json.loads(releases_response.text)
        oldest_release_age = (
            datetime.datetime.utcnow() -
            self.github.format_time(releases[-1]["published_at"])).days
        deployment_freq = round(oldest_release_age / len(releases), 2)
        print(
            f"In the last {max_age} days, got {len(releases)} minor releases. We release, on average, every",
            f"{deployment_freq} days.")

    def print_change_fail(self, max_age):
        # TODO change to all releases, not just minor
        # TODO fetch more releases if some bugs are older than the oldest retrieved release
        utcnow = datetime.datetime.utcnow()
        minor_releases = self.github.get_releases_since(
            self.github.get_minor_releases(), max_age)
        oldest_release_age = (
            utcnow -
            self.github.format_time(minor_releases[-1]["published_at"])).days
        # this query returns tickets created since the oldest considered release that are tagged as bug/support
        # with priority Highest/High
        jql_query = "(project = Silvercar) AND (type = Support OR type = Bug) AND " + \
            f"(priority = Highest OR priority = High) AND created > -{oldest_release_age}d order by createdDate DESC"
        bugs_response = self.jira.search_issue(jql_query=jql_query)
        bugs_count = len(json.loads(bugs_response.text))
        print(
            f"Got {len(minor_releases)} releases and {bugs_count} bugs since the oldest release",
            f"on {minor_releases[-1]['published_at']}, which is {oldest_release_age} days old."
        )
        print(
            f"  Change fail percentage (avg bugs/release) = {round(bugs_count / len(minor_releases), 3)}"
        )
Esempio n. 4
0
    def test_jitbit(self):

        # Use this to test a single post to JitBit.
        # Hardcoded key will be posted.

        process_key = 'AR-122'
        try:
            self.jira_api = JiraApi()

            # Check URL and user authentication
            if not self.jira_api.check_url_and_user():
                assert 'Check URL and User call failed!'

            filter_url = self.jira_api.get_filter_for_id(config.JIRA_FILTER_ID)

            # Run the filter and get a list of issues to deal with
            issues_list = self.jira_api.get_filter(filter_url)

            # Iterate over each issue and get details
            for issue in issues_list['issues']:

                key = issue['key']

                if key != process_key:
                    continue

                status, issue_info = self.jira_api.get_issue_info(key)

                if not status:
                    logger.critical(
                        '[{key}] ERROR: Not able to get issue info'.format(
                            key=key))
                    continue

                self.jira_api.get_attachment(key, issue_info)

                # Now create this issue in JitBit
                self._migrate_to_jitbit(key, issue_info)

        except Exception as e:
            logger.critical(e.message)
            raise
Esempio n. 5
0
    def jira_login(self):
        if not self.jira_api and settings.LOGIN and settings.PASSWORD:
            self.login = settings.LOGIN
            self.password = settings.PASSWORD
        else:
            self.login = input_req('Jira login: '******'Connection to jira failed')
        except JiraError:
            return self.error('Login or password is incorrect')
        else:
            if not permissions:
                return self.error('Jira permission denied')
Esempio n. 6
0
class ProcessData(object):
    def __init__(self):
        logger.info('Starting ProcessData ..')
        self.jitbit_api = JitbitApi()
        self.jira_api = JiraApi()

        # All assigned by is set to one user.
        self.default_assign_id = self.jitbit_api.get_user_id_by_email(
            config.JITBIT_DEFAULT_ASSIGN_EMAIL)

        self.start_time = time.time()

    def __del__(self):
        end_time = time.time()
        logger.info('Total time to run: {exec_time} seconds'.format(
            exec_time=end_time - self.start_time))

    def start(self):
        try:
            self.jira_api = JiraApi()

            # Check URL and user authentication
            if not self.jira_api.check_url_and_user():
                assert 'Check URL and User call failed!'

            # We want to run a specific filter in JIRA.
            # First give the filter Id and get the URL to run
            filter_url = self.jira_api.get_filter_for_id(config.JIRA_FILTER_ID)

            # Run the filter and get a list of issues to migrate
            issues_list = self.jira_api.get_filter(filter_url)

            # Setup progress bar
            total = len(issues_list['issues'])
            total_str = '/' + str(total)
            pbar_count = 0
            pbar = progressbar.ProgressBar(widgets=[
                progressbar.Bar(),
                progressbar.Counter(), total_str, ' ',
                progressbar.ETA(), ' ',
                progressbar.Timer()
            ],
                                           maxval=total).start()

            # Iterate over each issue and get details
            for issue in issues_list['issues']:

                key = issue['key']

                pbar_count += 1
                pbar.update(pbar_count)
                logger.info(
                    '[{key}] Processing: {pbar_count}{total_str}'.format(
                        key=key, pbar_count=pbar_count, total_str=total_str))

                status, issue_info = self.jira_api.get_issue_info(key)

                if not status:
                    logger.critical(
                        '[{key}] ERROR: Not able to get issue info'.format(
                            key=key))
                    continue

                self.jira_api.get_attachment(key, issue_info)

                # Now create this issue in JitBit
                self._migrate_to_jitbit(key, issue_info)

            pbar.finish

        except Exception as e:
            logger.critical(e.message)
            raise

    def test_jitbit(self):

        # Use this to test a single post to JitBit.
        # Hardcoded key will be posted.

        process_key = 'AR-122'
        try:
            self.jira_api = JiraApi()

            # Check URL and user authentication
            if not self.jira_api.check_url_and_user():
                assert 'Check URL and User call failed!'

            filter_url = self.jira_api.get_filter_for_id(config.JIRA_FILTER_ID)

            # Run the filter and get a list of issues to deal with
            issues_list = self.jira_api.get_filter(filter_url)

            # Iterate over each issue and get details
            for issue in issues_list['issues']:

                key = issue['key']

                if key != process_key:
                    continue

                status, issue_info = self.jira_api.get_issue_info(key)

                if not status:
                    logger.critical(
                        '[{key}] ERROR: Not able to get issue info'.format(
                            key=key))
                    continue

                self.jira_api.get_attachment(key, issue_info)

                # Now create this issue in JitBit
                self._migrate_to_jitbit(key, issue_info)

        except Exception as e:
            logger.critical(e.message)
            raise

    def _migrate_to_jitbit(self, key, issue_info):

        try:
            category_id = config.JITBIT_MIGRATE_CATEGORY_ID

            # We append JIRA key to subject
            subject = issue_info['fields']['summary'] + ' (' + key + ')'

            body = issue_info['fields']['description']
            if body is None:
                body = ''

            # We will set all of the priorities to Normal (0)
            priority_id = 0

            # Status. JitBit has only New(1) and Closed(3) exposed via API
            if (issue_info['fields']['status']['name']).lower() == 'closed':
                status_id = 3
            else:
                status_id = 1

            # Created by
            # By default this will be the person creating the ticket.
            # You can create 'on-behalf' of another person
            # This code will get the details of the user from JIRA

            # created_by_email = issue_info['fields']['creator']['emailAddress']
            # created_by_diplay_name = issue_info['fields']['creator']['displayName']
            # created_by_first_name, created_by_last_name = created_by_diplay_name.split(' ')

            # For our purpose we decided that all of the created by will be the
            # logged in user. So nothing to set.

            # Assigned to
            # We are assigning all to one person
            assign_to_id = self.default_assign_id

            # Ready to create the ticket
            ticket_id = int(
                self.jitbit_api.post_ticket(key, category_id, subject, body,
                                            priority_id))
            if ticket_id > 0:

                self.jitbit_api.post_set_assignee(key, ticket_id, assign_to_id)

                self._add_comments(key, ticket_id, issue_info)
                self._add_attachments(key, ticket_id, issue_info)

                # Status should be the last to be set. Otherwise JitBit will change it.
                self.jitbit_api.post_set_ticket_status(key, ticket_id,
                                                       status_id)

                # We update the issue on the JIRA side if the migration was successful.
                # We use the 'Tag' field in JIRA for this
                self.jira_api.post_tag(key, config.JITBIT_MIGRATION_SUCCESS)
            else:
                logger.critical(
                    'ERROR: could not create a ticket in JitBit for {key}'.
                    format(key=key))

        except Exception as e:

            # If we have a failure anywhere in the process we have to delete the ticket.
            # However, JitBit API does not support delete of tickets.
            # Instead move the ticket to 'Deleted' category.
            if ticket_id > 0:
                self.jitbit_api.post_mark_deleted(key, ticket_id)

            logger.critical(e.message)
            # Don't raise let continue

    def _add_comments(self, key, ticket_id, issue_info):
        assert ticket_id > 0

        comments = issue_info['fields']['comment']
        for comment in comments['comments']:
            comment_text = comment['body']
            # Comments can be anonymous
            comment_author = 'Unknown'
            if 'updateAuthor' in comment:
                comment_author = comment['updateAuthor']['emailAddress']

            comment_data = 'Updated by: ' + '[i]' + comment_author + '[/i]\n\n' + comment_text
            self.jitbit_api.post_comment(key, ticket_id, comment_data)

    def _add_attachments(self, key, ticket_id, issue_info):

        # We will add all the files under the <attachments>/key directory to this issue
        key_dir = os.path.join(config.ATTACHMENT_FOLDER, key)

        if not os.path.exists(key_dir):
            logger.info('[{key}] No attachments to process'.format(key=key))

        for root, sub_dirs, files in os.walk(key_dir, topdown=True):
            for filename in files:
                file_dir = os.path.join(root, filename)
                # Ignore attachments of size 5K or less. These are normally logos or icons that we can skip
                if os.path.getsize(file_dir) > 5120:
                    self.jitbit_api.post_attach_file(key, ticket_id, file_dir)
                else:
                    logger.info(
                        '[{key}] File size is < 5K. Ignoring. {file_dir}'.
                        format(key=key, file_dir=file_dir))
Esempio n. 7
0
 def __init__(self):
     self.github = GithubApi()
     self.circleci = CircleciApi()
     self.jira = JiraApi()
Esempio n. 8
0
class Application:
    def __init__(self):
        self.login = None
        self.password = None
        self.jira_api = None
        self.bitbucket_api = None
        self.project = None
        self.bb_project = None
        self.repo = None
        self.fix_version = None

    def run(self):
        print(styled('Bitbucket tools', bcolors.HEADER))

        if settings.LOGIN and settings.PASSWORD:
            self.jira_login()

        if settings.PROJECT:
            self.select_jira_project()

        if settings.BITBUCKET_PROJECT:
            self.select_bitbucket_project()

        if settings.BITBUCKET_REPOSITORY:
            self.select_bitbucket_repo()

        while True:
            menu = self.get_menu()
            answer = self.show_menu(menu)
            menu[answer]['command']()

    def show_menu(self, menu):
        questions = [item['title'] for item in menu]
        answer = input_list(questions) - 1
        return answer

    def get_menu(self):
        menu = []
        if not self.jira_api:
            menu.append({
                'title': 'Login jira',
                'command': self.jira_login
            })

            return menu

        menu.append({
            'title': 'Make release branch',
            'command': self.make_release_branch
        })
        menu.append({
            'title': 'Work with current branch',
            'command': self.modify_current_branch
        })

        if self.project:
            title = 'Change jira project [' + self.project['key'] + ']'
        else:
            title = 'Select jira project'
        menu.append({
            'title': title,
            'command': self.select_jira_project
        })

        if self.bb_project:
            title = 'Change bitbucket project [' + self.bb_project['key'] + ']'
        else:
            title = 'Select bitbucket project'
        menu.append({
            'title': title,
            'command': self.select_bitbucket_project
        })

        if self.repo:
            title = 'Change bitbucket repository [' + self.repo['name'] + ']'
        else:
            title = 'Select bitbucket repository'
        menu.append({
            'title': title,
            'command': self.select_bitbucket_repo
        })

        menu.append({
            'title': 'Change jira login [' + self.login + ']'
        })

        return menu

    def jira_login(self):
        if not self.jira_api and settings.LOGIN and settings.PASSWORD:
            self.login = settings.LOGIN
            self.password = settings.PASSWORD
        else:
            self.login = input_req('Jira login: '******'Connection to jira failed')
        except JiraError:
            return self.error('Login or password is incorrect')
        else:
            if not permissions:
                return self.error('Jira permission denied')

    def select_jira_project(self):
        try:
            projects = self.jira_api.get_projects()
        except JiraConnectionError:
            return self.error('Connection to jira failed')
        except JiraError:
            return self.error('Projects not found')

        if len(projects) == 0:
            return self.error('Projects not found')

        project = None
        if not self.project and settings.PROJECT:
            project = [project for project in projects if project['key'] == settings.PROJECT]
            if project:
                project = project[0]

        if not project:
            print('Select project')
            answer = input_list([project['name'] for project in projects])
            project = projects[answer - 1]

        self.project = project

    def select_bitbucket_project(self):
        try:
            projects = self.bitbucket_api.get_projects()
        except BitbucketConnectionError:
            return self.error('Connection to bitbucket failed')
        except BitbucketError:
            return self.error('Projects in BitBucket not found')

        if len(projects) == 0:
            return self.error('Projects in BitBucket not found')

        project = None
        if not self.bb_project and settings.BITBUCKET_PROJECT:
            project = [project for project in projects if project['key'] == settings.BITBUCKET_PROJECT]
            if project:
                project = project[0]

        if not project:
            print('Select BitBucket project:')
            answer = input_list([project['name'] for project in projects])
            project = projects[answer - 1]

        self.bb_project = project
        self.repo = None

    def select_bitbucket_repo(self):
        if not self.bb_project:
            self.select_bitbucket_project()

        try:
            repos = self.bitbucket_api.get_project_repos(self.bb_project['key'])
        except BitbucketConnectionError:
            return self.error('Connection to bitbucket failed')
        except BitbucketError:
            return self.error('Repositories in selected project not found')

        if len(repos) == 0:
            return self.error('Repositories in selected project not found')

        repo = None
        if not self.repo and settings.BITBUCKET_REPOSITORY:
            repo = [repo for repo in repos if repo['name'] == settings.BITBUCKET_REPOSITORY]
            if repo:
                repo = repo[0]

        if not repo:
            print('Select repository')
            answer = input_list([repo['name'] for repo in repos])
            repo = repos[answer - 1]

        self.repo = repo

    def select_fix_version(self):
        if not self.project:
            self.select_jira_project()

        try:
            versions = self.jira_api.get_project_versions(self.project['key'])
        except JiraConnectionError:
            return self.error('Connection to jira failed')
        except JiraError:
            return self.error('Fix versions for selected project not found')

        if len(versions) == 0:
            return self.error('Fix versions for selected project not found')

        versions = [version for version in versions if not version['released']]
        answer = input_list([version['name'] for version in versions])
        version = versions[answer - 1]

        self.fix_version = version

    def make_release_branch(self):
        if not self.project or not self.bb_project or not self.repo:
            print(styled('Select jira project, bitbucket project and bitbucket repostiory', bcolors.ERROR))
            return

        self.select_fix_version()
        commits_controller = CommitsController(
            self.jira_api, self.bitbucket_api, self.project,
            self.bb_project, self.repo, self.fix_version
        )
        commits_controller.run()

    def modify_current_branch(self):
        if not self.project or not self.bb_project or not self.repo:
            print(styled('Select jira project, bitbucket project and bitbucket repostiory', bcolors.ERROR))
            return

        commits_controller = CommitsController(
            self.jira_api, self.bitbucket_api,
            self.bb_project, self.project, self.repo
        )
        commits_controller.run()

    def error(self, message):
        exit(styled(message, bcolors.ERROR))