Example #1
0
    def __init__(self, repo_owner, repo, environment):

        self.output = OutputHelper()
        if all([repo_owner, repo, environment]):
            self._repo_owner = repo_owner
            self._repo = repo
            self._environment = environment.upper()
        else:
            exit('\nMissing github param\n\nABORTING!\n\n')

        self._url_github_api = self._get_url_github_api(
            HOST_GITHUB, repo_owner, repo)

        self._token_string = self._get_token_string(ACCESS_TOKEN)

        url = self._get_url_github_api_tags(self._url_github_api,
                                            self._token_string)

        req = self._get_tags(url)

        tags = req.json()
        self._max_comparisons = self._get_max_comparisons(tags)
        self._latest_tags = self._get_latest_tags(tags)
        self._last_tag = self._get_last_tag()
        self._last_tag_version = self._last_tag[VERS]
Example #2
0
    def __init__(self, host, bugzilla_username, bugzilla_password):

        self.output = OutputHelper()
        self.host = host
        self.username = bugzilla_username
        self.password = bugzilla_password
        self.token = self.get_token(host)
Example #3
0
    def __init__(self, repo='', product='', release_num='', environment=''):

        if all([repo, product, release_num, environment]):
            self.repo = repo
            self.product = product
            self.release_num = release_num
            self.environment = environment
        else:
            self.set_args()

        self.api_url = self.get_api_url()
        self.tags = self.get_tags()
        self.num_comparisons = self.get_num_comparisons(self.tags)
        self.latest_tags = self.get_latest_tags()
        self.output = OutputHelper()
Example #4
0
class BugzillaRESTAPI(object):
    """"Used for CRUD operations against Bugzilla REST API

    Currently only supports authentication and create NEW.
    TODO: add bug update

    Use against URL_BUGZILLA_DEV to test.
    """

    def __init__(self, host, bugzilla_username, bugzilla_password):

        self.output = OutputHelper()
        self.host = host
        self.username = bugzilla_username
        self.password = bugzilla_password
        self.token = self.get_token(host)


    def get_json(self, release_num, product, environment, status, description):
        """Create bugzilla JSON to POST to REST API.

        Returns:
            JSON string
        """

        data = {
            'product':'Mozilla Services',
            'component':'General',
            'version':'unspecified',
            'op_sys':'All',
            'rep_platform':'All'
        }
        short_desc = 'Please deploy {} {} to {}'.format(
            release_num, product, environment)
        data.update(
            {'short_desc': short_desc,
             'description': description, 'status': status}
        )
        return data


    # TODO(rpapa): it would save authentication time to cache token for re-use
    #              add a try / except & query a new one if expired
    def get_token(self, host):
        """Fetch and return bugzilla token.

        Returns:
            string token
        """

        url = '{}/rest/login?login={}&password={}'.format(
            host, self.username, self.password)
        req = requests.get(url)
        decoded = json.loads(req.text)
        try:
            if 'token' not in decoded:
                raise InvalidCredentials
        except InvalidCredentials:
            err_header = self.output.get_header('BUGZILLA ERROR')

            err_msg = '{}\n{}\n{}\n\n'.format(
            err_header,
            decoded['message'],
            decoded['documentation'])

            sys.exit(err_msg)
        else:
            return decoded['token']


    def create_bug(
            self, release_num, product, environment, status, description):
        """Create bugzilla bug with description

        Note:
            On bugzilla-dev - available status:
            UNCONFIRMED, NEW, ASSIGNED, RESOLVED

        Returns:
            json string to POST to REST API
        """

        url = '{}/rest/bug?token={}'.format(self.host, self.token)
        req = requests.post(url)
        data = self.get_json(
            release_num, product, environment, status, description)
        print data

        headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
        req = requests.post(url, data=json.dumps(data), headers=headers)
        print 'CREATE BUG: {}'.format(req.status_code)
        return req.text
Example #5
0
class GithubAPI(object):
    """Used for GET operations against github API.
    """

    def __init__(self, repo='', product='', release_num='', environment=''):

        if all([repo, product, release_num, environment]):
            self.repo = repo
            self.product = product
            self.release_num = release_num
            self.environment = environment
        else:
            self.set_args()

        self.api_url = self.get_api_url()
        self.tags = self.get_tags()
        self.num_comparisons = self.get_num_comparisons(self.tags)
        self.latest_tags = self.get_latest_tags()
        self.output = OutputHelper()



    def set_args(self):

        parser = argparse.ArgumentParser(
            description='Scripts for creating / updating deployment tickets in \
            Bugzilla',
            formatter_class=argparse.ArgumentDefaultsHelpFormatter)

        parser.add_argument(
            '-r', '--repo',
            default='mozilla-services',
            required=True)

        parser.add_argument(
            '-p', '--product',
            help='Example: loop-server',
            required=True)

        # @TODO: Need to add code to restrict query to <= release_num
        parser.add_argument(
            '-n', '--release-num',
            help='Example: 0.14.3',
            required=True)

        parser.add_argument(
            '-e', '--environment',
            help='Enter: stage, prod',
            default='stage',
            required=False)

        args = vars(parser.parse_args())

        self.repo = args['repo']
        self.product = args['product']
        self.release_num = args['release_num']
        self.environment = args['environment']


    def get_api_url(self):
        """Return github API URL as string"""

        url = 'https://api.{}/repos/{}/{}/git/'.format(HOST_GITHUB, \
                  self.repo, self.product)
        return url

    def get_url_data(self, url):
        req = requests.get(url)
        try:
            if 'Not Found' in req.text:
                raise NotFoundError
        except NotFoundError:
            err_header = self.output.get_header('ERROR')
            err_msg = '{}\nNot found at: \n{}\nABORTING!\n\n'.format(
                err_header,
                self.api_url)
            sys.exit(err_msg)
        else:
            return json.loads(req.text)

    def get_tags(self):
        """Get all tags as json from Github API."""
        return self.get_url_data(self.api_url + 'refs/tags')

    def get_tag(self, sha):
        """Get a specific tag's data from Github API."""
        return self.get_url_data(self.api_url + 'tags/' + sha)

    def get_latest_tags(self):
        """Github API can only return all tags, but we only want the latest.

        Return:
            list of lists containing: [release_num, git sha] for last tags
        """

        start = len(self.tags) - self.num_comparisons
        tags = self.tags
        latest = []
        for i in xrange(len(tags)):
            if i >= start:
                parts = tags[i]['ref'].split('/')
                release_num = parts[2]
                sha = tags[i]['object']['sha']
                tag = [release_num, sha]
                latest.append(tag)
        return latest


    def get_url_tag_release(self, release_num):
        """Return github tag release URL as string"""

        url = 'https://{}/{}/{}/releases/tag/{}'.format(
            HOST_GITHUB,
            self.repo,
            self.product,
            release_num
        )
        return url


    def get_url_tag_commit(self, git_sha):
        """Return github tag commit SHA URL as string"""

        url = 'https://{}/{}/{}/commit/{}'.format(
            HOST_GITHUB,
            self.repo,
            self.product,
            git_sha
        )
        return url


    def get_comparison(self, start, end):
        """Return github compare URL as string"""

        return 'https://{}/{}/{}/compare/{}...{}'.format(HOST_GITHUB, \
            self.repo, self.product, start, end) + '\n'

    def get_num_comparisons(self, tags):
        """Display up to: MAX_COMPARISONS_TO_SHOW (if we have that many tags).
        If not, display comparisons of all tags.

        Returns:
            integer - num of github release comparisons to display
        """

        count = len(tags)
        if count >= MAX_COMPARISONS_TO_SHOW:
            return MAX_COMPARISONS_TO_SHOW
        return count


    def get_changelog(self, commit_sha):
        """"Parse CHANGELOG for latest tag.

        Return:
            String with log from latest tag.
        """

        url = 'https://{}/{}/{}/' + commit_sha + '/CHANGELOG'
        url = url.format(HOST_GITHUB_RAW, self.repo, self.product)

        req = requests.get(url)
        lines = req.text

        first = self.latest_tags[self.num_comparisons - 1][VERS]
        last = self.latest_tags[self.num_comparisons - 2][VERS]
        flag = False

        log = ''
        for line in lines.splitlines():
            if first in line:
                flag = True
            if last in line:
                flag = False
            if flag:
                log += line + '\n'
        return log


    def get_release_notes(self):
        """Constructs release notes for Bugzilla service deployment ticket.

        Returns:
            String - with release notes
        """

        notes = self.output.get_header('RELEASE NOTES')
        notes += 'https://{}/{}/{}/releases'.format(HOST_GITHUB, \
                                                    self.repo, self.product) + '\n'

        notes += self.output.get_sub_header('COMPARISONS')
        notes += self.get_comparison(self.latest_tags[0][VERS],
                                     self.latest_tags[1][VERS])

        if len(self.latest_tags) >= (MAX_COMPARISONS_TO_SHOW - 1):
            notes += self.get_comparison(self.latest_tags[1][VERS],
                                         self.latest_tags[2][VERS])

        if len(self.latest_tags) >= MAX_COMPARISONS_TO_SHOW:
            notes += self.get_comparison(self.latest_tags[2][VERS],
                                         self.latest_tags[3][VERS])

        tag_data = self.get_tag(self.latest_tags[3][SHA])

        notes += self.output.get_sub_header('TAGS')
        notes += self.get_url_tag_release(self.latest_tags[3][VERS]) + '\n'
        notes += self.get_url_tag_commit(tag_data["object"]["sha"]) + '\n'

        changelog = self.get_changelog(tag_data["object"]["sha"])
        if changelog:
            notes += self.output.get_sub_header('CHANGELOG')
            notes += changelog
        return notes
Example #6
0
class ReleaseNotes(object):
    """Used for GET operations against github API."""
    def __init__(self, repo_owner, repo, environment):

        self.output = OutputHelper()
        if all([repo_owner, repo, environment]):
            self._repo_owner = repo_owner
            self._repo = repo
            self._environment = environment.upper()
        else:
            exit('\nMissing github param\n\nABORTING!\n\n')

        self._url_github_api = self._get_url_github_api(
            HOST_GITHUB, repo_owner, repo)

        self._token_string = self._get_token_string(ACCESS_TOKEN)

        url = self._get_url_github_api_tags(self._url_github_api,
                                            self._token_string)

        req = self._get_tags(url)

        tags = req.json()
        self._max_comparisons = self._get_max_comparisons(tags)
        self._latest_tags = self._get_latest_tags(tags)
        self._last_tag = self._get_last_tag()
        self._last_tag_version = self._last_tag[VERS]

    @property
    def last_tag(self):
        return self._last_tag_version

    def _get_last_tag(self):
        """Return last tag"""

        return self._latest_tags[self._max_comparisons - 1]

    def _get_token_string(self, access_token):
        """Return access_token as url param (if exists)"""

        if access_token:
            return '?access_token={0}'.format(access_token)
        return ''

    def _get_url_github(self, host_github, repo_owner, repo):
        """Return github root URL as string"""

        return 'https://{0}/{1}/{2}'.format(host_github, repo_owner, repo)

    def _get_url_github_api(self, host_github, repo_owner, repo):
        """Return github API URL as string"""

        return 'https://api.{0}/repos/{1}/{2}/git'.format(
            host_github, repo_owner, repo)

    def _get_url_github_api_tags(self, url_github_api, token_string):

        return '{0}/refs/tags{1}'.format(url_github_api, token_string)

    def _get_url_changelog(self, url_github_raw, commit_sha, filename):

        return '{0}/{1}/{2}'.format(url_github_raw, commit_sha, filename)

    def _url_last_tag(self, url_github_api, last_tag_sha, token_string):

        return '{0}/tags/{1}{2}'.format(url_github_api, last_tag_sha,
                                        token_string)

    def _url_comparison(self, url_github, start, end):

        return '{0}/compare/{1}...{2}'.format(url_github, start, end)

    def _url_tag(self, url_github, tag_version):

        return '{0}/releases/tag/{1}'.format(url_github, tag_version)

    def _url_tag_commit(self, url_github, commit_sha):

        return '{0}/commit/{1}'.format(url_github, commit_sha)

    def _url_releases(self, url_github):

        return '{0}/releases'.format(url_github)

    def _get_max_comparisons(self, tags):
        """Calculates max comparisons to show

        Note:
            Display up to MAX_COMPARISONS_TO_SHOW (or less)

        Returns:
            integer - num of github release comparisons to display
        """

        count = len(tags)
        if count >= MAX_COMPARISONS_TO_SHOW:
            return MAX_COMPARISONS_TO_SHOW
        else:
            return count

    def _get_tags(self, url):
        """Get all tags as json from Github API."""

        req = requests.get(url)
        try:
            if 'Not Found' in req.text:
                raise NotFoundError
        except NotFoundError:
            err_header = self.output.get_header('ERROR')
            err_msg = '{0}\nNothing found at: \n{1}\nABORTING!\n\n'.format(
                err_header, url)
            sys.exit(err_msg)
        else:
            return req

    def _parse_tag(self, tag):
        """Parse a tag object for the data we want

        Return:
            list of desired elements
        """

        parts = tag['ref'].split('/')
        release_num = parts[2]
        sha = tag['object']['sha']
        type = tag['object']['type']
        url = tag['object']['url'] + self._token_string
        creation_date = self._get_commit_date(url)
        self.output.log((release_num, creation_date))
        return [release_num, sha, type, url, creation_date]

    def _get_latest_tags(self, tags):
        """Github API returns all tags indiscriminately, but
        we only want the latest.

        Return:
            list of lists containing:
        [release_num, sha, type, url, creation_date] for latest tags
        """

        self.output.log('Retrieve all tags', True)
        start = len(tags) - self._max_comparisons
        tags_unsorted = []
        for i in range(len(tags)):
            tag = self._parse_tag(tags[i])
            tags_unsorted.append(tag)

        self.output.log('Sort tags by commit date', True)
        tags_sorted = sorted(tags_unsorted,
                             key=lambda tags_sorted: tags_sorted[4])
        self.output.log('DONE!')

        latest = []
        self.output.log('Get last tags from sorted list', True)
        for i in range(len(tags_sorted)):
            if i >= start:
                latest.append(tags_sorted[i])
                self.output.log(tags_sorted[i])
        self.output.log(latest)
        return latest

    def _get_commit_sha(self):
        """Return tag commit sha as string.

        Note:
            Varies depending on object type: type='tag' or 'commit'
            type='tag' requires a secondary call to retrieve commit url"""

        last_tag = self._last_tag
        if last_tag[TYPE] == 'tag':
            url = self._url_last_tag(self._url_github_api, last_tag[SHA],
                                     self._token_string)
            req = self._get_tags(url)
            return req.json()['object']['sha']
        else:
            return last_tag[SHA]

    def _get_commit_date(self, url):
        """Return tag or commit creation date as string."""

        req = self._get_tags(url)
        if 'git/tags' in url:
            return req.json()['tagger']['date'].split('T')[0]
        else:
            return req.json()['committer']['date'].split('T')[0]

    def _get_changelog(self, commit_sha):
        """"Parse and return CHANGELOG for latest tag as string"""

        url_github_raw = self._get_url_github(HOST_GITHUB_RAW,
                                              self._repo_owner, self._repo)

        for filename in CHANGELOG_FILENAMES:
            url = self._get_url_changelog(url_github_raw, commit_sha, filename)
            req = requests.get(url)
            try:
                if 'Not Found' in req.text:
                    raise NotFoundError
            except NotFoundError:
                pass
            else:
                break

        if req.text == 'Not Found':
            return ''

        lines = req.text

        # parse out release notes for this release only
        # only works if version numbers in changelog appear exactly
        # as they are tagged
        vers_latest = self._latest_tags[self._max_comparisons - 1][VERS]
        vers_previous = self._latest_tags[self._max_comparisons - 2][VERS]

        flag = False

        log = ''
        for line in lines.splitlines():
            if vers_latest in line:
                flag = True
            if vers_previous in line:
                flag = False
            if flag:
                log += line + '\n'
        return log

    def _get_section_release_notes(self, url_github):
        """Return bugzilla release notes with header as string"""

        notes = self.output.get_header('RELEASE NOTES')
        notes += self._url_releases(url_github) + '\n'
        return notes

    def _get_section_comparisons(self, url_github):
        """Return release notes - COMPARISONS section as string"""

        notes = self.output.get_sub_header('COMPARISONS')

        for i in range(0, self._max_comparisons - 1):
            start = self._latest_tags[i][VERS]
            end = self._latest_tags[i + 1][VERS]
            notes += self._url_comparison(url_github, start, end) + '\n'
        self.output.log('comparisons section - DONE!')
        return notes

    def _get_section_tags(self, url_github):
        """Return release notes - TAGS section as string"""

        commit_sha = self._get_commit_sha()
        notes = self.output.get_sub_header('TAGS')

        notes += self._url_tag(
            url_github,
            self._latest_tags[self._max_comparisons - 1][VERS]) + '\n'
        notes += self._url_tag_commit(url_github, commit_sha) + '\n'

        notes += self._get_section_changelog(commit_sha)
        self.output.log('tags section - DONE!')
        return notes

    def _get_section_changelog(self, commit_sha):
        """Return release notes - CHANGELOG section as string"""

        changelog = self._get_changelog(commit_sha)
        self.output.log('changelog section - DONE!')
        if changelog:
            return self.output.get_sub_header('CHANGELOG') + changelog
        else:
            return ''

    def get_release_notes(self):
        """Return release notes for Bugzilla deployment ticket as string"""

        url_github = self._get_url_github(HOST_GITHUB, self._repo_owner,
                                          self._repo)

        self.output.log('Create release notes', True)
        notes = self._get_section_release_notes(url_github)
        notes += self._get_section_comparisons(url_github)
        notes += self._get_section_tags(url_github)
        return notes