示例#1
0
 def __init__(self,
              user_map_helper=None,
              label_mapping_filename=None,
              milestone_mapping_filename=None):
     self.github_conn = None
     self.comments_service = None
     self.milestone_manager = MilestoneHelper(milestone_mapping_filename)
     self.label_helper = LabelHelper(label_mapping_filename)
     self.jinja_env = Environment(
         loader=PackageLoader('github_issues', 'templates'))
     self.user_map_helper = user_map_helper
 def __init__(self, user_map_helper=None, label_mapping_filename=None, milestone_mapping_filename=None):
     self.github_conn = None
     self.comments_service = None
     self.milestone_manager = MilestoneHelper(milestone_mapping_filename)
     self.label_helper = LabelHelper(label_mapping_filename)
     self.jinja_env = Environment(loader=PackageLoader("github_issues", "templates"))
     self.user_map_helper = user_map_helper
class GithubIssueMaker:
    """
    Given a Redmine issue in JSON format, create a GitHub issue.
    These issues should be moved from Redmine in order of issue.id.  This will allow mapping of Redmine issue ID's against newly created Github issued IDs.  e.g., can translate related issues numbers, etc.
    """

    ISSUE_STATE_CLOSED = ["Rejected", "Closed", "Resolved"]

    def __init__(self, user_map_helper=None, label_mapping_filename=None, milestone_mapping_filename=None):
        self.github_conn = None
        self.comments_service = None
        self.milestone_manager = MilestoneHelper(milestone_mapping_filename)
        self.label_helper = LabelHelper(label_mapping_filename)
        self.jinja_env = Environment(loader=PackageLoader("github_issues", "templates"))
        self.user_map_helper = user_map_helper

    def get_comments_service(self):
        if self.comments_service is None:
            self.comments_service = pygithub3.services.issues.Comments(**get_github_auth())

        return self.comments_service

    def get_github_conn(self):

        if self.github_conn is None:
            self.github_conn = pygithub3.Github(**get_github_auth())
        return self.github_conn

    def format_name_for_github(self, author_name, include_at_sign=True):
        """
        (1) Try the user map
        (2) If no match, return the name
        """
        if not author_name:
            return None

        if self.user_map_helper:
            github_name = self.user_map_helper.get_github_user(author_name, include_at_sign)
            if github_name is not None:
                return github_name
        return author_name

    def get_redmine_assignee_name(self, redmine_issue_dict):
        """
        If a redmine user has a github account mapped, add the person as the assignee

        "assigned_to": {
            "id": 4,
            "name": "Philip Durbin"
        },
        /cc @kneath @jresig
        """
        if not type(redmine_issue_dict) is dict:
            return None

        redmine_name = redmine_issue_dict.get("assigned_to", {}).get("name", None)
        if redmine_name is None:
            return None

        return redmine_name

    def get_assignee(self, redmine_issue_dict):
        """
        If a redmine user has a github account mapped, add the person as the assignee

        "assigned_to": {
            "id": 4,
            "name": "Philip Durbin"
        },
        /cc @kneath @jresig
        """
        if not type(redmine_issue_dict) is dict:
            return None

        redmine_name = redmine_issue_dict.get("assigned_to", {}).get("name", None)
        if redmine_name is None:
            return None

        github_username = self.format_name_for_github(redmine_name, include_at_sign=False)

        return github_username

    def update_github_issue_with_related(self, redmine_json_fname, redmine2github_issue_map):
        """
        Update a GitHub issue with related tickets as specfied in Redmine

        - Read the current github description
        - Add related notes to the bottom of description
        - Update the description

        "relations": [
              {
                  "delay": null,
                  "issue_to_id": 4160,
                  "issue_id": 4062,
                  "id": 438,
                  "relation_type": "relates"
              },
              {
                  "delay": null,
                  "issue_to_id": 3643,
                  "issue_id": 4160,
                  "id": 439,
                  "relation_type": "relates"
              }
          ],
          "id": 4160,
        """
        if not os.path.isfile(redmine_json_fname):
            msgx("ERROR.  update_github_issue_with_related. file not found: %s" % redmine_json_fname)

        # msg('issue map: %s' % redmine2github_issue_map)

        json_str = open(redmine_json_fname, "rU").read()
        rd = json.loads(json_str)  # The redmine issue as a python dict
        # msg('rd: %s' % rd)

        if rd.get("relations", None) is None:
            msg("no relations")
            return

        redmine_issue_num = rd.get("id", None)
        if redmine_issue_num is None:
            return

        github_issue_num = redmine2github_issue_map.get(str(redmine_issue_num), None)
        if github_issue_num is None:
            msg("Redmine issue not in nap")
            return

        # Related tickets under 'relations'
        #
        github_related_tickets = []
        original_related_tickets = []
        for rel in rd.get("relations"):
            issue_to_id = rel.get("issue_to_id", None)
            if issue_to_id is None:
                continue
            if rd.get("id") == issue_to_id:  # skip relations pointing to this ticket
                continue

            original_related_tickets.append(issue_to_id)
            related_github_issue_num = redmine2github_issue_map.get(str(issue_to_id), None)
            msg(related_github_issue_num)
            if related_github_issue_num:
                github_related_tickets.append(related_github_issue_num)
        github_related_tickets.sort()
        original_related_tickets.sort()
        #
        # end: Related tickets under 'relations'

        # Related tickets under 'children'
        #
        # "children": [{ "tracker": {"id": 2, "name": "Feature"    }, "id": 3454, "subject": "Icons in results and facet"    }, ...]
        #
        github_child_tickets = []
        original_child_tickets = []

        child_ticket_info = rd.get("children", [])
        if child_ticket_info:
            for ctick in child_ticket_info:

                child_id = ctick.get("id", None)
                if child_id is None:
                    continue

                original_child_tickets.append(child_id)
                child_github_issue_num = redmine2github_issue_map.get(str(child_id), None)

                msg(child_github_issue_num)
                if child_github_issue_num:
                    github_child_tickets.append(child_github_issue_num)
            original_child_tickets.sort()
            github_child_tickets.sort()
        #
        # end: Related tickets under 'children'

        #
        # Update github issue with related and child tickets
        #
        #
        if len(original_related_tickets) == 0 and len(original_child_tickets) == 0:
            return

        # Format related ticket numbers
        #
        original_issues_formatted = [
            """[%s](%s)""" % (x, self.format_redmine_issue_link(x)) for x in original_related_tickets
        ]
        original_issues_str = ", ".join(original_issues_formatted)

        related_issues_formatted = ["#%d" % x for x in github_related_tickets]
        related_issue_str = ", ".join(related_issues_formatted)
        msg("Redmine related issues: %s" % original_issues_str)
        msg("Github related issues: %s" % related_issue_str)

        # Format children ticket numbers
        #
        original_children_formatted = [
            """[%s](%s)""" % (x, self.format_redmine_issue_link(x)) for x in original_child_tickets
        ]
        original_children_str = ", ".join(original_children_formatted)

        github_children_formatted = ["#%d" % x for x in github_child_tickets]
        github_children_str = ", ".join(github_children_formatted)
        msg("Redmine sub-issues: %s" % original_children_str)
        msg("Github sub-issues: %s" % github_children_str)

        try:
            issue = self.get_github_conn().issues.get(number=github_issue_num)
        except pygithub3.exceptions.NotFound:
            msg("Issue not found!")
            return

        template = self.jinja_env.get_template("related_issues.md")

        template_params = {
            "original_description": issue.body,
            "original_issues": original_issues_str,
            "related_issues": related_issue_str,
            "child_issues_original": original_children_str,
            "child_issues_github": github_children_str,
        }

        updated_description = template.render(template_params)

        issue = self.get_github_conn().issues.update(number=github_issue_num, data={"body": updated_description})

        msg("Issue updated!")  #' % issue.body)

    def format_redmine_issue_link(self, issue_id):
        if issue_id is None:
            return None

        return os.path.join(REDMINE_SERVER, "issues", "%d" % issue_id)

    def close_github_issue(self, github_issue_num):

        if not github_issue_num:
            return False
        msgt("Close issue: %s" % github_issue_num)

        try:
            issue = self.get_github_conn().issues.get(number=github_issue_num)
        except pygithub3.exceptions.NotFound:
            msg("Issue not found!")
            return False

        if issue.state in self.ISSUE_STATE_CLOSED:
            msg("Already closed")
            return True

        updated_issue = self.get_github_conn().issues.update(number=github_issue_num, data={"state": "closed"})
        if not updated_issue:
            msg("Failed to close issue")
            return False

        if updated_issue.state in self.ISSUE_STATE_CLOSED:
            msg("Issue closed")
            return True

        msg("Failed to close issue")
        return False

    def make_github_issue(self, redmine_json_fname, **kwargs):
        """
        Create a GitHub issue from JSON for a Redmine issue.

        - Format the GitHub description to include original redmine info: author, link back to redmine ticket, etc
        - Add/Create Labels
        - Add/Create Milestones
        """
        if not os.path.isfile(redmine_json_fname):
            msgx("ERROR.  make_github_issue. file not found: %s" % redmine_json_fname)

        include_comments = kwargs.get("include_comments", True)
        include_assignee = kwargs.get("include_assignee", True)

        json_str = open(redmine_json_fname, "rU").read()
        rd = json.loads(json_str)  # The redmine issue as a python dict

        # msg(json.dumps(rd, indent=4))
        msg("Attempt to create issue: [#%s][%s]" % (rd.get("id"), rd.get("subject")))

        # (1) Format the github issue description
        #
        #
        template = self.jinja_env.get_template("description.md")

        author_name = rd.get("author", {}).get("name", None)
        author_github_username = self.format_name_for_github(author_name)

        desc_dict = {
            "description": translate_for_github(rd.get("description", "no description")),
            "redmine_link": self.format_redmine_issue_link(rd.get("id")),
            "redmine_issue_num": rd.get("id"),
            "start_date": rd.get("start_date", None),
            "author_name": author_name,
            "author_github_username": author_github_username,
            "redmine_assignee": self.get_redmine_assignee_name(rd),
        }

        description_info = template.render(desc_dict)

        #
        # (2) Create the dictionary for the GitHub issue--for the github API
        #
        # self.label_helper.clear_labels(151)
        github_issue_dict = {
            "title": rd.get("subject"),
            "body": description_info,
            "labels": self.label_helper.get_label_names_from_issue(rd),
        }

        milestone_number = self.milestone_manager.get_create_milestone(rd)
        if milestone_number:
            github_issue_dict["milestone"] = milestone_number

        if include_assignee:
            assignee = self.get_assignee(rd)
            if assignee:
                github_issue_dict["assignee"] = assignee

        msg(github_issue_dict)

        #
        # (3) Create the issue on github
        #

        issue_obj = self.get_github_conn().issues.create(github_issue_dict)
        # issue_obj = self.get_github_conn().issues.update(151, github_issue_dict)

        msgt("Github issue created: %s" % issue_obj.number)
        msg("issue id: %s" % issue_obj.id)
        msg("issue url: %s" % issue_obj.html_url)

        # Map the new github Issue number to the redmine issue number
        #
        # redmine2github_id_map.update({ rd.get('id', 'unknown') : issue_obj.number })

        # print( redmine2github_id_map)

        #
        # (4) Add the redmine comments (journals) as github comments
        #
        if include_comments:
            journals = rd.get("journals", None)
            if journals:
                self.add_comments_for_issue(issue_obj.number, journals)

        #
        #   (5) Should this issue be closed?
        #
        if self.is_redmine_issue_closed(rd):
            self.close_github_issue(issue_obj.number)

        return issue_obj.number

    def is_redmine_issue_closed(self, redmine_issue_dict):
        """
        "status": {
            "id": 5,
            "name": "Completed"
        },
        """
        if not type(redmine_issue_dict) == dict:
            return False

        status_info = redmine_issue_dict.get("status", None)
        if not status_info:
            return False

        if status_info.has_key("name") and status_info.get("name", None) in self.ISSUE_STATE_CLOSED:
            return True

        return False

    def add_comments_for_issue(self, issue_num, journals):
        """
        Add comments
        """
        if journals is None:
            msg("no journals")
            return

        comment_template = self.jinja_env.get_template("comment.md")

        for j in journals:
            notes = j.get("notes", None)
            if not notes:
                continue

            author_name = j.get("user", {}).get("name", None)
            author_github_username = self.format_name_for_github(author_name)

            note_dict = {
                "description": translate_for_github(notes),
                "note_date": j.get("created_on", None),
                "author_name": author_name,
                "author_github_username": author_github_username,
            }
            comment_info = comment_template.render(note_dict)

            comment_obj = None
            try:
                comment_obj = self.get_comments_service().create(issue_num, comment_info)
            except requests.exceptions.HTTPError as e:
                msgt("Error creating comment: %s" % e.message)
                continue

            if comment_obj:
                dashes()
                msg("comment created")

                msg("comment id: %s" % comment_obj.id)
                msg("api issue_url: %s" % comment_obj.issue_url)
                msg("api comment url: %s" % comment_obj.url)
                msg("html_url: %s" % comment_obj.html_url)
示例#4
0
class GithubIssueMaker:
    """
    Given a Redmine issue in JSON format, create a GitHub issue.
    These issues should be moved from Redmine in order of issue.id.  This will allow mapping of Redmine issue ID's against newly created Github issued IDs.  e.g., can translate related issues numbers, etc.
    """
    ISSUE_STATE_CLOSED = 'closed'

    def __init__(self,
                 user_map_helper=None,
                 label_mapping_filename=None,
                 milestone_mapping_filename=None):
        self.github_conn = None
        self.comments_service = None
        self.milestone_manager = MilestoneHelper(milestone_mapping_filename)
        self.label_helper = LabelHelper(label_mapping_filename)
        self.jinja_env = Environment(
            loader=PackageLoader('github_issues', 'templates'))
        self.user_map_helper = user_map_helper

    def get_comments_service(self):
        if self.comments_service is None:
            self.comments_service = pygithub3.services.issues.Comments(
                **get_github_auth())

        return self.comments_service

    def get_github_conn(self):

        if self.github_conn is None:
            self.github_conn = pygithub3.Github(**get_github_auth())
        return self.github_conn

    def format_name_for_github(self, author_name, include_at_sign=True):
        """
        (1) Try the user map
        (2) If no match, return the name
        """
        if not author_name:
            return None

        if self.user_map_helper:
            github_name = self.user_map_helper.get_github_user(
                author_name, include_at_sign)
            if github_name is not None:
                return github_name
        return author_name

    def get_redmine_assignee_name(self, redmine_issue_dict):
        """
        If a redmine user has a github account mapped, add the person as the assignee
        
        "assigned_to": {
            "id": 4, 
            "name": "Philip Durbin"
        },
        /cc @kneath @jresig
        """
        if not type(redmine_issue_dict) is dict:
            return None

        redmine_name = redmine_issue_dict.get('assigned_to',
                                              {}).get('name', None)
        if redmine_name is None:
            return None

        return redmine_name

    def get_assignee(self, redmine_issue_dict):
        """
        If a redmine user has a github account mapped, add the person as the assignee
        
        "assigned_to": {
            "id": 4, 
            "name": "Philip Durbin"
        },
        /cc @kneath @jresig
        """
        if not type(redmine_issue_dict) is dict:
            return None

        redmine_name = redmine_issue_dict.get('assigned_to',
                                              {}).get('name', None)
        if redmine_name is None:
            return None

        github_username = self.format_name_for_github(redmine_name,
                                                      include_at_sign=False)

        return github_username

    def update_github_issue_with_related(self, redmine_json_fname,
                                         redmine2github_issue_map):
        """
        Update a GitHub issue with related tickets as specfied in Redmine
        
        - Read the current github description
        - Add related notes to the bottom of description
        - Update the description
        
        "relations": [
              {
                  "delay": null, 
                  "issue_to_id": 4160, 
                  "issue_id": 4062, 
                  "id": 438, 
                  "relation_type": "relates"
              }, 
              {
                  "delay": null, 
                  "issue_to_id": 3643, 
                  "issue_id": 4160, 
                  "id": 439, 
                  "relation_type": "relates"
              }
          ], 
          "id": 4160,
        """
        if not os.path.isfile(redmine_json_fname):
            msgx('ERROR.  update_github_issue_with_related. file not found: %s'
                 % redmine_json_fname)

        #msg('issue map: %s' % redmine2github_issue_map)

        json_str = open(redmine_json_fname, 'rU').read()
        rd = json.loads(json_str)  # The redmine issue as a python dict
        #msg('rd: %s' % rd)

        if rd.get('relations', None) is None:
            msg('no relations')
            return

        redmine_issue_num = rd.get('id', None)
        if redmine_issue_num is None:
            return

        github_issue_num = redmine2github_issue_map.get(
            str(redmine_issue_num), None)
        if github_issue_num is None:
            msg('Redmine issue not in nap')
            return

        # Related tickets under 'relations'
        #
        github_related_tickets = []
        original_related_tickets = []
        for rel in rd.get('relations'):
            issue_to_id = rel.get('issue_to_id', None)
            if issue_to_id is None:
                continue
            if rd.get(
                    'id'
            ) == issue_to_id:  # skip relations pointing to this ticket
                continue

            original_related_tickets.append(issue_to_id)
            related_github_issue_num = redmine2github_issue_map.get(
                str(issue_to_id), None)
            msg(related_github_issue_num)
            if related_github_issue_num:
                github_related_tickets.append(related_github_issue_num)
        github_related_tickets.sort()
        original_related_tickets.sort()
        #
        # end: Related tickets under 'relations'

        # Related tickets under 'children'
        #
        # "children": [{ "tracker": {"id": 2, "name": "Feature"    }, "id": 3454, "subject": "Icons in results and facet"    }, ...]
        #
        github_child_tickets = []
        original_child_tickets = []

        child_ticket_info = rd.get('children', [])
        if child_ticket_info:
            for ctick in child_ticket_info:

                child_id = ctick.get('id', None)
                if child_id is None:
                    continue

                original_child_tickets.append(child_id)
                child_github_issue_num = redmine2github_issue_map.get(
                    str(child_id), None)

                msg(child_github_issue_num)
                if child_github_issue_num:
                    github_child_tickets.append(child_github_issue_num)
            original_child_tickets.sort()
            github_child_tickets.sort()
        #
        # end: Related tickets under 'children'

        #
        # Update github issue with related and child tickets
        #
        #
        if len(original_related_tickets) == 0 and len(
                original_child_tickets) == 0:
            return

        # Format related ticket numbers
        #
        original_issues_formatted = [
            """[%s](%s)""" % (x, self.format_redmine_issue_link(x))
            for x in original_related_tickets
        ]
        original_issues_str = ', '.join(original_issues_formatted)

        related_issues_formatted = ['#%d' % x for x in github_related_tickets]
        related_issue_str = ', '.join(related_issues_formatted)
        msg('Redmine related issues: %s' % original_issues_str)
        msg('Github related issues: %s' % related_issue_str)

        # Format children ticket numbers
        #
        original_children_formatted = [
            """[%s](%s)""" % (x, self.format_redmine_issue_link(x))
            for x in original_child_tickets
        ]
        original_children_str = ', '.join(original_children_formatted)

        github_children_formatted = ['#%d' % x for x in github_child_tickets]
        github_children_str = ', '.join(github_children_formatted)
        msg('Redmine sub-issues: %s' % original_children_str)
        msg('Github sub-issues: %s' % github_children_str)

        try:
            issue = self.get_github_conn().issues.get(number=github_issue_num)
        except pygithub3.exceptions.NotFound:
            msg('Issue not found!')
            return

        template = self.jinja_env.get_template('related_issues.md')

        template_params = { 'original_description' : issue.body\
                            , 'original_issues' : original_issues_str\
                            , 'related_issues' : related_issue_str\
                            , 'child_issues_original' : original_children_str\
                            , 'child_issues_github' : github_children_str\

                            }

        updated_description = template.render(template_params)

        issue = self.get_github_conn().issues.update(
            number=github_issue_num, data={'body': updated_description})

        msg('Issue updated!')  #' % issue.body)

    def format_redmine_issue_link(self, issue_id):
        if issue_id is None:
            return None

        return os.path.join(REDMINE_SERVER, 'issues', '%d' % issue_id)

    def close_github_issue(self, github_issue_num):

        if not github_issue_num:
            return False
        msgt('Close issue: %s' % github_issue_num)

        try:
            issue = self.get_github_conn().issues.get(number=github_issue_num)
        except pygithub3.exceptions.NotFound:
            msg('Issue not found!')
            return False

        if issue.state == self.ISSUE_STATE_CLOSED:
            msg('Already closed')
            return True

        updated_issue = self.get_github_conn().issues.update(
            number=github_issue_num, data={'state': self.ISSUE_STATE_CLOSED})
        if not updated_issue:
            msg('Failed to close issue')
            return False

        if updated_issue.state == self.ISSUE_STATE_CLOSED:
            msg('Issue closed')
            return True

        msg('Failed to close issue')
        return False

    def make_github_issue(self, redmine_json_fname, **kwargs):
        """
        Create a GitHub issue from JSON for a Redmine issue.
        
        - Format the GitHub description to include original redmine info: author, link back to redmine ticket, etc
        - Add/Create Labels
        - Add/Create Milestones
        """
        if not os.path.isfile(redmine_json_fname):
            msgx('ERROR.  make_github_issue. file not found: %s' %
                 redmine_json_fname)

        include_comments = kwargs.get('include_comments', True)
        include_assignee = kwargs.get('include_assignee', True)

        json_str = open(redmine_json_fname, 'rU').read()
        rd = json.loads(json_str)  # The redmine issue as a python dict

        #msg(json.dumps(rd, indent=4))
        msg('Attempt to create issue: [#%s][%s]' %
            (rd.get('id'), rd.get('subject')))

        # (1) Format the github issue description
        #
        #
        template = self.jinja_env.get_template('description.md')

        author_name = rd.get('author', {}).get('name', None)
        author_github_username = self.format_name_for_github(author_name)

        desc_dict = {'description' : translate_for_github(rd.get('description', 'no description'))\
                    , 'redmine_link' : self.format_redmine_issue_link(rd.get('id'))\
                    , 'redmine_issue_num' : rd.get('id')\
                    , 'start_date' : rd.get('start_date', None)\
                    , 'author_name' : author_name\
                    , 'author_github_username' : author_github_username\
                    , 'redmine_assignee' : self.get_redmine_assignee_name(rd)
        }

        description_info = template.render(desc_dict)

        #
        # (2) Create the dictionary for the GitHub issue--for the github API
        #
        #self.label_helper.clear_labels(151)
        github_issue_dict = { 'title': rd.get('subject')\
                    , 'body' : description_info\
                    , 'labels' : self.label_helper.get_label_names_from_issue(rd)
                    }

        milestone_number = self.milestone_manager.get_create_milestone(rd)
        if milestone_number:
            github_issue_dict['milestone'] = milestone_number

        if include_assignee:
            assignee = self.get_assignee(rd)
            if assignee:
                github_issue_dict['assignee'] = assignee

        msg(github_issue_dict)

        #
        # (3) Create the issue on github
        #

        issue_obj = self.get_github_conn().issues.create(github_issue_dict)
        #issue_obj = self.get_github_conn().issues.update(151, github_issue_dict)

        msgt('Github issue created: %s' % issue_obj.number)
        msg('issue id: %s' % issue_obj.id)
        msg('issue url: %s' % issue_obj.html_url)

        # Map the new github Issue number to the redmine issue number
        #
        #redmine2github_id_map.update({ rd.get('id', 'unknown') : issue_obj.number })

        #print( redmine2github_id_map)

        #
        # (4) Add the redmine comments (journals) as github comments
        #
        if include_comments:
            journals = rd.get('journals', None)
            if journals:
                self.add_comments_for_issue(issue_obj.number, journals)

        #
        #   (5) Should this issue be closed?
        #
        if self.is_redmine_issue_closed(rd):
            self.close_github_issue(issue_obj.number)

        return issue_obj.number

    def is_redmine_issue_closed(self, redmine_issue_dict):
        """
        "status": {
            "id": 5, 
            "name": "Completed"
        },
        """
        if not type(redmine_issue_dict) == dict:
            return False

        status_info = redmine_issue_dict.get('status', None)
        if not status_info:
            return False

        if status_info.has_key('id') and status_info.get('id', None) == 5:
            return True

        return False

    def add_comments_for_issue(self, issue_num, journals):
        """
        Add comments
        """
        if journals is None:
            msg('no journals')
            return

        comment_template = self.jinja_env.get_template('comment.md')

        for j in journals:
            notes = j.get('notes', None)
            if not notes:
                continue

            author_name = j.get('user', {}).get('name', None)
            author_github_username = self.format_name_for_github(author_name)

            note_dict = { 'description' : translate_for_github(notes)\
                         , 'note_date' : j.get('created_on', None)\
                         , 'author_name' : author_name\
                         , 'author_github_username' : author_github_username\
                         }
            comment_info = comment_template.render(note_dict)

            comment_obj = None
            try:
                comment_obj = self.get_comments_service().create(
                    issue_num, comment_info)
            except requests.exceptions.HTTPError as e:
                msgt('Error creating comment: %s' % e.message)
                continue

            if comment_obj:
                dashes()
                msg('comment created')

                msg('comment id: %s' % comment_obj.id)
                msg('api issue_url: %s' % comment_obj.issue_url)
                msg('api comment url: %s' % comment_obj.url)
                msg('html_url: %s' % comment_obj.html_url)