#85 Adding ReadTheDocs documentation
Merged 4 years ago by sidpremkumar. Opened 4 years ago by sidpremkumar.
sidpremkumar/sync-to-jira adding-docs-support  into  develop

Add ReadTheDocs documentation
sidpremkumar • 4 years ago  
file modified
+1
@@ -12,3 +12,4 @@ 

  venv/*

  venv3/*

  xunit-tests.xml

+ docs/build/*

@@ -0,0 +1,1 @@ 

+ sphinx_rtd_theme 

\ No newline at end of file

@@ -0,0 +1,25 @@ 

+ Adding New Repos Guide 

+ =======================

+ 

+ Have you ever wanted to add new upstream repos? Well now you can! 

+ 

+ 1. First ensure that your upstream repo is on the Fed Message Bus

+ 2. Now add two new functions to `upstream.py`

+     * :code:`def hande_REPO-NAME_message(msg, config)`

+         * This function will take in a fedmessage message (msg) and the config dict

+         * This function will return a sync2jira.Intermediary.Issue object

+         * This function will be used when listening to the message bus

+     * :code:`def REPO-NAME_issues(upstream, config)`

+         * This function will take in an upstream repo name and the config dict 

+         * This function will return a generator of sync2jira.Intermediary.Issue objects that contain all upstream Issues

+         * This function will be used to initialize and sync upstream/downstream issues

+ 3. Now modify the `main.py` functions: 

+     * :code:`def initialize(config)`

+         * Add another section (like Pagure and GitHub) to utlize the :code:`REPO-NAME_issues` function you just made. 

+     * :code:`def listen(config)`

+         * Add another section to the if statement under Pagure and GitHub

+             * :code:`elif 'REPO-NAME' in suffix:`

+             * Now utilize the :code:`handle_REPO-NAME_message` function you just made

+ 4. If all goes smoothly, your new repo should work with Sync2Jira!

+ 

+ .. note:: If you want to submit a Pull Request, ensure that you add appropriate Unit Tests, Tox is passing, and you have appropriate documentation!

file added
+185
@@ -0,0 +1,185 @@ 

+ # -*- coding: utf-8 -*-

+ #

+ # Configuration file for the Sphinx documentation builder.

+ #

+ # This file does only contain a selection of the most common options. For a

+ # full list see the documentation:

+ # http://www.sphinx-doc.org/en/master/config

+ 

+ # -- Path setup --------------------------------------------------------------

+ 

+ # If extensions (or modules to document with autodoc) are in another directory,

+ # add these directories to sys.path here. If the directory is relative to the

+ # documentation root, use os.path.abspath to make it absolute, like shown here.

+ #

+ # import os

+ # import sys

+ # sys.path.insert(0, os.path.abspath('.'))

+ 

+ 

+ # -- Project information -----------------------------------------------------

+ 

+ project = u'Sync2Jira'

+ copyright = u'2019, Ralph Bean'

+ author = u'Ralph Bean'

+ 

+ # The short X.Y version

+ version = u''

+ # The full version, including alpha/beta/rc tags

+ release = u'2.0'

+ 

+ 

+ # -- General configuration ---------------------------------------------------

+ 

+ # If your documentation needs a minimal Sphinx version, state it here.

+ #

+ # needs_sphinx = '1.0'

+ 

+ # Add any Sphinx extension module names here, as strings. They can be

+ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom

+ # ones.

+ extensions = [

+     'sphinx.ext.autodoc',

+     'sphinx.ext.doctest',

+     'sphinx.ext.intersphinx',

+     'sphinx.ext.todo',

+     'sphinx.ext.coverage',

+     'sphinx.ext.mathjax',

+     'sphinx.ext.ifconfig',

+     'sphinx.ext.viewcode',

+     'sphinx.ext.githubpages',

+ ]

+ 

+ # Add any paths that contain templates here, relative to this directory.

+ templates_path = ['ntemplates']

+ 

+ # The suffix(es) of source filenames.

+ # You can specify multiple suffix as a list of string:

+ #

+ # source_suffix = ['.rst', '.md']

+ source_suffix = '.rst'

+ 

+ # The master toctree document.

+ master_doc = 'index'

+ 

+ # The language for content autogenerated by Sphinx. Refer to documentation

+ # for a list of supported languages.

+ #

+ # This is also used if you do content translation via gettext catalogs.

+ # Usually you set "language" from the command line for these cases.

+ language = None

+ 

+ # List of patterns, relative to source directory, that match files and

+ # directories to ignore when looking for source files.

+ # This pattern also affects html_static_path and html_extra_path.

+ exclude_patterns = []

+ 

+ # The name of the Pygments (syntax highlighting) style to use.

+ pygments_style = None

+ 

+ 

+ # -- Options for HTML output -------------------------------------------------

+ 

+ # The theme to use for HTML and HTML Help pages.  See the documentation for

+ # a list of builtin themes.

+ #

+ html_theme = 'sphinx_rtd_theme'

+ 

+ # Theme options are theme-specific and customize the look and feel of a theme

+ # further.  For a list of options available for each theme, see the

+ # documentation.

+ #

+ # html_theme_options = {}

+ 

+ # Add any paths that contain custom static files (such as style sheets) here,

+ # relative to this directory. They are copied after the builtin static files,

+ # so a file named "default.css" will overwrite the builtin "default.css".

+ html_static_path = ['nstatic']

+ 

+ # Custom sidebar templates, must be a dictionary that maps document names

+ # to template names.

+ #

+ # The default sidebars (for documents that don't match any pattern) are

+ # defined by theme itself.  Builtin themes are using these templates by

+ # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',

+ # 'searchbox.html']``.

+ #

+ html_sidebars = { '**': ['globaltoc.html', 'searchbox.html'] }

+ 

+ 

+ # -- Options for HTMLHelp output ---------------------------------------------

+ 

+ # Output file base name for HTML help builder.

+ htmlhelp_basename = 'Sync2Jiradoc'

+ 

+ 

+ # -- Options for LaTeX output ------------------------------------------------

+ 

+ latex_elements = {

+     # The paper size ('letterpaper' or 'a4paper').

+     #

+     # 'papersize': 'letterpaper',

+ 

+     # The font size ('10pt', '11pt' or '12pt').

+     #

+     # 'pointsize': '10pt',

+ 

+     # Additional stuff for the LaTeX preamble.

+     #

+     # 'preamble': '',

+ 

+     # Latex figure (float) alignment

+     #

+     # 'figure_align': 'htbp',

+ }

+ 

+ # Grouping the document tree into LaTeX files. List of tuples

+ # (source start file, target name, title,

+ #  author, documentclass [howto, manual, or own class]).

+ latex_documents = [

+     (master_doc, 'Sync2Jira.tex', u'Sync2Jira Documentation',

+      u'Ralph Bean', 'manual'),

+ ]

+ 

+ 

+ # -- Options for manual page output ------------------------------------------

+ 

+ # One entry per manual page. List of tuples

+ # (source start file, name, description, authors, manual section).

+ man_pages = [

+     (master_doc, 'sync2jira', u'Sync2Jira Documentation',

+      [author], 1)

+ ]

+ 

+ 

+ # -- Options for Texinfo output ----------------------------------------------

+ 

+ # Grouping the document tree into Texinfo files. List of tuples

+ # (source start file, target name, title, author,

+ #  dir menu entry, description, category)

+ texinfo_documents = [

+     (master_doc, 'Sync2Jira', u'Sync2Jira Documentation',

+      author, 'Sync2Jira', 'One line description of project.',

+      'Miscellaneous'),

+ ]

+ 

+ 

+ # -- Options for Epub output -------------------------------------------------

+ 

+ # Bibliographic Dublin Core info.

+ epub_title = project

+ 

+ # The unique identifier of the text. This can be a ISBN number

+ # or the project homepage.

+ #

+ # epub_identifier = ''

+ 

+ # A unique identification for the text.

+ #

+ # epub_uid = ''

+ 

+ # A list of files that should not be packed into the epub file.

+ epub_exclude_files = ['search.html']

+ 

+ 

+ # -- Extension configuration ------------------------------------------------- 

\ No newline at end of file

@@ -0,0 +1,108 @@ 

+ Config File 

+ ===========

+ The config file is made up of multiple parts 

+ 

+ .. code-block:: python

+ 

+     'admins': ['demo_jira_username']

+ 

+ * Admins can be users who manage Sync2Jira. They will be cc'd in any emails regarding duplicate issues found.

+   

+ .. code-block:: python

+ 

+     'initialize': True

+ 

+ * Initilization set to True will ensure that there is an inital sync done when Sync2Jira starts.

+   It is recommended to leave this as True to ensure that all issues are in sync. 

+ 

+ .. code-block:: python

+ 

+     'testing': True

+ 

+ * Testing is a flag that will determine if any changes are actually made downstream (on JIRA tickets). 

+   Set to false if you are developing and don't want any changes to take effect.

+ 

+ .. code-block:: python

+ 

+   'github_token': 'YOUR_TOKEN', 

+ 

+ * This is where you can enter your GitHub API token. 

+ 

+ .. code-block:: python

+ 

+     'default_jira_instance': 'example'

+ 

+ * This is the default JIRA instance to be used if none is provided in the project.

+ 

+ .. code-block:: python

+ 

+     'jira': {

+         'example': {

+             'options': {

+                 'server': 'https://some_jira_server_somewhere.com',

+                 'verify': True,

+             },

+             'basic_auth': ('YOU_USERNAME', 'YOUR_PASSWORD'),

+         },

+     },

+ 

+ * Here you can configure multiple JIRA instances if you have projects with differing downstream JIRA instances.

+   Ensure to name them approproialty, in name of the JIRA instance above is `example`.

+ 

+ .. code-block:: python

+ 

+     'map': {

+             'pagure': {

+                 'Demo_project': {'project': 'FACTORY', 'component': 'gitbz',

+                                 'updates': [...], 'owner': 'jira_username'},

+                 # 'koji': { 'project': 'BREW', 'component': None, },

+             },

+             'github': {

+                 'GITHUB_USERNAME/Demo_project': {'project': 'FACTORY', 'component': 'gitbz',

+                                                 'updates': [...], 'owner': 'jira_username'},

+             },

+         },

+ 

+ * You can add your projects here. The 'project' field is associated with downstream JIRA projects, and 'component' with downstream components. 

+   You can add the following to the updates array: 

+     

+     * :code:`'comments'` 

+         * Sync comments and comment edits

+     * :code:`{'tags': {'overwrite': True/False}}` 

+         * Sync tags, do/don't overwrite downstream tags

+     * :code:`{'fixVersion'; {'overwrite': True/False}}`

+         * Sync fixVersion (downstream milestone), do/don't overwrite downstream fixVersion

+     * :code:`{'assignee': {'overwrite': True/False}}` 

+         * Sync assignee (for Github only the first assignee will sync) do/don't overwrite downstream assignee

+     * :code:`'description'` 

+         * Sync description

+     * :code:`'title'`

+         * Sync title

+     * :code:`{'transition': True/'CUSTOM_TRANSITION'}` 

+         * Sync status (open/closed), Sync only status/Attempt to transition JIRA ticket to CUSTOM_TRANSITION on upstream closure

+     

+     .. note:: 

+         

+         :Overwrite: Setting this to :code:`True` will ensure that Upstream (GitHub or Pagure) values will overwrite downstream ones (i.e. if its empty upstream it'll be empty downstream)

+         :CUSTOM_TRANSITION: Setting this value will get Sync2Jira to automatially transition downstream tickets once their upstream counterparts get closed. Set this to whatever 'closed' means downstream.

+ 

+ * It is strongly encouraged for teams to use the :code:`owner` field. If configured, owners will be alerted if Sync2Jira finds dupliate downstream issues. 

+   Further the owner will be used as a default in case the program is unable to find a valid assignee. 

+ 

+ .. code-block:: python

+ 

+     'filters': {

+             'github': {

+                 # Only sync multi-type tickets from bodhi.

+                 'fedora-infra/bodhi': {'state': 'open', 'milestone': 4, },

+             },

+         }

+ 

+ * You can also add filters per-project. The following can be added to the filter dict: 

+ 

+     * :code:`status`

+         * Open/Closed 

+     * :code:`tags`

+         * List of tags to look for

+     * :code:`milestone`

+         * Upstream milestone status 

\ No newline at end of file

@@ -0,0 +1,8 @@ 

+ Downstream

+ ============

+ 

+ .. automodule:: sync2jira.downstream

+     :members:

+     :private-members:

+ 

+ 

@@ -0,0 +1,34 @@ 

+ Sync2Jira documentation

+ =====================================

+ 

+ 

+ Home

+ ^^^^^^^^^^^^^^^^^^

+ 

+ * :ref:`search`

+ 

+ 

+ .. toctree::

+    :maxdepth: 2

+    :caption: Setup Guide

+ 

+    quickstart

+    config-file

+ 

+ 

+ .. toctree::

+    :maxdepth: 1

+    :caption: Adding New Repos

+ 

+    adding-new-repo-guide

+ 

+ 

+ .. toctree::

+    :maxdepth: 5

+    :caption: Code Documentation

+ 

+    main

+    upstream

+    downstream

+    intermediary

+    mailer 

\ No newline at end of file

@@ -0,0 +1,127 @@ 

+ Intermediary

+ ============

+ 

+ Sync2Jira converts upstream issues into custom Issue objects. You can see the __init__ below: 

+ 

+ .. code-block:: python

+ 

+         def __init__(self, source, title, url, upstream, comments,

+                  config, tags, fixVersion, priority, content,

+                  reporter, assignee, status, id, downstream=None):

+             self.source = source

+             self._title = title

+             self.url = url

+             self.upstream = upstream

+             self.comments = comments

+             self.tags = tags

+             self.fixVersion = fixVersion

+             self.priority = priority

+             self.content = content

+             self.reporter = reporter

+             self.assignee = assignee

+             self.status = status

+             self.id = str(id)

+             if not downstream:

+                 self.downstream = config['sync2jira']['map'][self.source][upstream]

+             else:

+                 self.downstream = downstream

+ 

+ The object has two properties: 

+ 

+ .. code-block:: python

+ 

+     @property

+     def title(self):

+         return u'[%s] %s' % (self.upstream, self._title)

+ 

+ * This will return the title in downstream form (i.e. [UPSTREAM_REPO] TITLE)

+ 

+ .. code-block:: python

+     

+     @property

+     def upstream_title(self):

+         return self._title

+ 

+ * This will return the raw title (i.e. TITLE)

+ 

+ Currently the service is set up create Issue objects from Pagure and Github: 

+ 

+ .. code-block:: python

+ 

+     @classmethod

+     def from_pagure(cls, upstream, issue, config):

+         base = config['sync2jira'].get('pagure_url', 'https://pagure.io')

+         comments = []

+         for comment in issue['comments']:

+             # Only add comments that are not Metadata updates

+             if '**Metadata Update' in comment['comment']:

+                 continue

+             # Else add the comment

+             # Convert the date to datetime

+             comment['date_created'] = datetime.fromtimestamp(float(comment['date_created']))

+             comments.append({

+                 'author': comment['user']['name'],

+                 'body': comment['comment'],

+                 'name': comment['user']['name'],

+                 'id': comment['id'],

+                 'date_created': comment['date_created'],

+                 'changed': None

+             })

+ 

+         return Issue(

+             source='pagure',

+             title=issue['title'],

+             url=base + '/%s/issue/%i' % (upstream, issue['id']),

+             upstream=upstream,

+             config=config,

+             comments=comments,

+             tags=issue['tags'],

+             fixVersion=[issue['milestone']],

+             priority=issue['priority'],

+             content=issue['content'],

+             reporter=issue['user'],

+             assignee=issue['assignee'],

+             status=issue['status'],

+             id=issue['date_created']

+         )

+     

+ .. code-block:: python

+ 

+     @classmethod

+     def from_github(cls, upstream, issue, config):

+         comments = []

+         for comment in issue['comments']:

+             comments.append({

+                 'author': comment['author'],

+                 'name': comment['name'],

+                 'body': comment['body'],

+                 'id': comment['id'],

+                 'date_created': comment['date_created'],

+                 'changed': None

+             })

+ 

+         # Reformat the state field

+         if issue['state']:

+             if issue['state'] == 'open':

+                 issue['state'] = 'Open'

+             elif issue['state'] == 'closed':

+                 issue['state'] = 'Closed'

+ 

+         # TODO: Priority is broken

+         return Issue(

+             source='github',

+             title=issue['title'],

+             url=issue['html_url'],

+             upstream=upstream,

+             config=config,

+             comments=comments,

+             tags=issue['labels'],

+             fixVersion=[issue['milestone']],

+             priority=None,

+             content=issue['body'],

+             reporter=issue['user'],

+             assignee=issue['assignees'],

+             status=issue['state'],

+             id=issue['id']

+         )

+ .. note:: Currently Priority is broken and is a known issue. 

@@ -0,0 +1,6 @@ 

+ Mailer

+ =======

+ 

+ .. automodule:: sync2jira.mailer

+     :members:

+ 

@@ -0,0 +1,5 @@ 

+ Main

+ ====

+ 

+ .. automodule:: sync2jira.main

+     :members:

@@ -0,0 +1,71 @@ 

+ Quick Start

+ ============

+ 

+ Want to quickly get started working with Sync2Jira? Follow these steps: 

+ 

+ 1. **First open up** :code:`fedmsg.d/sync2jira.py`

+ 

+ 2. Enter your GitHub token which you can get `here <https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line>`_

+     .. code-block:: python

+         

+         'github_token': 'YOUR_TOKEN',

+ 

+ 3. Enter relevent JIRA information

+     .. code-block:: python

+         

+         'default_jira_instance': 'example',

+         'jira': {

+             'example': {

+                 'options': {

+                     'server': 'https://some_jira_server_somewhere.com',

+                     'verify': True,

+                 },

+                 'basic_auth': ('YOU_USERNAME', 'YOUR_PASSWORD'),

+             },

+         },

+     

+     .. note:: You might have to set verify to False

+ 

+ 4. Add your upstream repos to the `map` section

+     .. code-block:: python

+         

+         'map': {

+             'pagure': {

+                 'Demo_project': {'project': 'FACTORY', 'component': 'gitbz',

+                                  'updates': [...]},

+                 # 'koji': { 'project': 'BREW', 'component': None, },

+             },

+             'github': {

+                 'GITHUB_USERNAME/Demo_project': {'project': 'FACTORY', 'component': 'gitbz',

+                                                  'updates': [...]},

+             },

+         },

+     

+     .. note:: You can learn more about what can go into the updates list `here <config-file.html>`_

+ 

+ 5. Finally you can tweak the config files optional settings to your liking

+     .. code-block:: python

+ 

+         # Admins to be cc'd in duplicate emails

+             'admins': ['demo_jira_username'],

+         # Scrape sources at startup

+             'initialize': True,

+         # Don't actually make changes to JIRA...

+             'testing': True,

+         

+         'filters': {

+                 'github': {

+                     # Only sync multi-type tickets from bodhi.

+                     'fedora-infra/bodhi': {'state': 'open', 'milestone': 4, },

+                 },

+             }

+ 6. Now that you're done with the config file you can install sync2jira and run 

+     .. code-block:: shell 

+ 

+         python setup.py install

+         >> ....

+         >> Finished processing dependencies for sync2jira==1.7

+         sync2jira

+     .. note:: You might have to add `config['validate_signatures'] = False`. 

+               You can find out more under the `main <main.html#main-anchor>`_.

+ 

@@ -0,0 +1,5 @@ 

+ Upstream

+ ============

+ 

+ .. automodule:: sync2jira.upstream

+     :members:

file modified
+172 -166
@@ -45,11 +45,11 @@ 

  

  def _comment_format(comment):

      """

-     Function to format JIRA comments

-     Args:

-         comment (dict): Upstream comment

-     Returns:

-         Response (str): Comments formatted

+     Function to format JIRA comments.

+ 

+     :param dict comment: Upstream comment

+     :returns: Comments formatted

+     :rtype: String

      """

      pretty_date = comment['date_created'].strftime("%a %b %d")

      return "[%s] Upstream, %s wrote [%s]:\n\n{quote}\n%s\n{quote}" % (
@@ -60,11 +60,11 @@ 

      """

      Legacy function to format JIRA comments.

      This is still used to match comments so no

-     duplicates are created

-     Args:

-         comment (dict): Upstream comment

-     Returns:

-         Response (str): Comments formatted

+     duplicates are created.

+ 

+     :param dict comment: Upstream comment

+     :returns: Comments formatted

+     :rtype: String

      """

      return "Upstream, %s wrote:\n\n{quote}\n%s\n{quote}" % (

          comment['name'], comment['body'])
@@ -72,12 +72,12 @@ 

  

  def _get_jira_client(issue, config):

      """

-     Function to match and create JIRA client

-     Args:

-         issue (sync2jira.intermediary.Issue): Issue object

-         config (dict): Config dict

-     Returns:

-         client (jira.client.JIRA): Matching JIRA client

+     Function to match and create JIRA client.

+ 

+     :param sync2jira.intermediary.Issue issue: Issue object

+     :param dict config: Config dict

+     :returns: Matching JIRA client

+     :rtype: jira.client.JIRA

      """

      # The name of the jira instance to use is stored under the 'map'

      # key in the config where each upstream is mapped to jira projects.
@@ -104,14 +104,14 @@ 

  

  def _matching_jira_issue_query(client, issue, config, free=False):

      """

-     API calls that find matching JIRA tickets if any are present

-     Args:

-         client (jira.client.JIRA): JIRA client

-         issue (sync2jira.intermediary.Issue): Issue object

-         config (dict): Config dict

-         free (Bool): Free tag to add 'statusCategory != Done' to query

-     Returns:

-         results (lst): Returns a list of matching JIRA issues if any are found

+     API calls that find matching JIRA tickets if any are present.

+ 

+     :param jira.client.JIRA client: JIRA client

+     :param sync2jira.intermediary.Issue issue: Issue object

+     :param Dict config: Config dict

+     :param Bool free: Free tag to add 'statusCategory != Done' to query

+     :returns: results: Returns a list of matching JIRA issues if any are found

+     :rtype: List

      """

      # Searches for any remote link to the issue.url

      query = 'issueFunction in linkedIssuesOfRemote("%s") and ' \
@@ -177,15 +177,14 @@ 

  def alert_user_of_duplicate_issues(issue, final_result, results_of_query,

                                     config, client):

      """

-     Alerts owner of duplicate downstream issues

-     Args:

-         issue (sync2jira.intermediate.Issue): Upstream Issue object

-         final_result (list): Issue selected by matching algorithm

-         results_of_query (list): Result of JQL query

-         config (dict): Config dict

-         client (jira.client.JIRA): JIRA client

-     Returns:

-         Nothing

+     Alerts owner of duplicate downstream issues.

+ 

+     :param sync2jira.intermediate.Issue issue: Upstream Issue object

+     :param List final_result: Issue selected by matching algorithm

+     :param List results_of_query: Result of JQL query

+     :param Dict config: Config dict

+     :param jira.client.JIRA client: JIRA client

+     :returns: Nothing

      """

      # First remove final_result from results_of_query

      results_of_query.remove(final_result[0])
@@ -273,12 +272,12 @@ 

  

  def find_username(issue, config):

      """

-     Finds JIRA username for an issue object

-     Args:

-         issue (sync2jira.intermediary.Issue): Issue object

-         config (dict): Config dict

-     Returns:

-         return (str): Username string

+     Finds JIRA username for an issue object.

+ 

+     :param sync2jira.intermediary.Issue issue: Issue object

+     :param Dict config: Config dict

+     :returns: Username string

+     :rtype: String

      """

      jira_instance = issue.downstream.get('jira_instance', False)

      if not jira_instance:
@@ -292,16 +291,14 @@ 

  def check_comments_for_duplicate(client, result, username):

      """

      Checks comment of JIRA issue to see if it has been

-     marked as a duplicate

-     Args:

-         client (jira.client.JIRA): JIRA client)

-         result (jira.resource.Issue): JIRA issue

-         username (str): Username of JIRA user

-     Returns:

-         return (bool): True if duplicate comment was not found

-         *Or*

-         return (jira.resource.Issue): JIRA issue if we were able to

-                                       find it

+     marked as a duplicate.

+ 

+     :param jira.client.JIRA client: JIRA client)

+     :param jira.resource.Issue result: JIRA issue

+     :param string username: Username of JIRA user

+     :returns: True if duplicate comment was not found or JIRA issue if \

+               we were able to find it

+     :rtype: Bool or jira.resource.Issue

      """

      for comment in client.comments(result):

          search = re.search(r'Marking as duplicate of (\w*)-(\d*)',
@@ -314,12 +311,12 @@ 

  

  def _find_comment_in_jira(comment, j_comments):

      """

-     Helper function to filter out comments that are matching

-     Args:

-         comment (dict): Individual comment from upstream

-         j_comments (lst(jira.resources.Comment)): Comments from JIRA downstream

-     Returns:

-         Item/None (jira.resource.Comment)

+     Helper function to filter out comments that are matching.

+ 

+     :param Dict comment: Individual comment from upstream

+     :param List j_comments: Comments from JIRA downstream

+     :returns: Item/None

+     :rtype: jira.resource.Comment/None

      """

      formatted_comment = _comment_format(comment)

      legacy_formatted_comment = _comment_format_legacy(comment)
@@ -349,12 +346,12 @@ 

  

  def _comment_matching(g_comments, j_comments):

      """

-     Function to filter out comments that are matching

-     Args:

-         g_comments (lst(dict)): Comments from Issue object

-         j_comments (lst(jira.resources.Comment)): Comments from JIRA downstream

-     Returns:

-         response (lst): Returns a list of comments that are not matching

+     Function to filter out comments that are matching.

+ 

+     :param List g_comments: Comments from Issue object

+     :param List j_comments: Comments from JIRA downstream

+     :returns: Returns a list of comments that are not matching

+     :rtype: List

      """

      return list(

          filter(
@@ -366,14 +363,14 @@ 

  

  def _get_existing_jira_issue(client, issue, config):

      """

-     Get a jira issue by the linked remote issue.

+     Get a jira issue by the linked remote issue. \

      This is the new supported way of doing this.

-     Args:

-         client (jira.client.JIRA): JIRA client

-         issue (sync2jira.intermediary.Issue): Issue object

-         config (dict): Config dict

-     Returns:

-         response (lst): Returns a list of matching JIRA issues if any are found

+ 

+     :param jira.client.JIRA client: JIRA client

+     :param sync2jira.intermediary.Issue issue: Issue object

+     :param Dict config: Config dict

+     :returns: Returns a list of matching JIRA issues if any are found

+     :rtype: List

      """

      results = _matching_jira_issue_query(client, issue, config)

      if results:
@@ -383,9 +380,10 @@ 

  

  

  def _get_existing_jira_issue_legacy(client, issue, config):

-     """ This is our old way of matching issues: use the special url field.

- 

+     """

+     This is our old way of matching issues: use the special url field.

      This will be phased out and removed in a future release.

+ 

      """

  

      kwargs = dict(issue.downstream.items())
@@ -405,17 +403,13 @@ 

  

  def _attach_link(client, downstream, remote_link):

      """

-     Attaches the upstream link to the JIRA ticket

-     Args:

-         client (jira.client.JIRA): JIRA client

-         downstream (jira.resources.Issue): Response from

-                                            creating the

-                                            JIRA ticket

-         remote_link():

-     Returns:

-         downstream (jira.resources.Issue): Response from

-                                            creating the

-                                            JIRA ticket

+     Attaches the upstream link to the JIRA ticket.

+ 

+     :param jira.client.JIRA client: JIRA client

+     :param jira.resources.Issue downstream: Response from creating the JIRA ticket

+     :param str remote_link: Remote link

+     :return: downstream: Response from creating the JIRA ticket

+     :rtype: jira.resources.Issue

      """

      log.info("   Attaching tracking link %r to %r", remote_link, downstream.key)

      modified_desc = downstream.fields.description + " "
@@ -438,9 +432,9 @@ 

  

  

  def _upgrade_jira_issue(client, downstream, issue, config):

-     """ Given an old legacy-style downstream issue...

+     """

+     Given an old legacy-style downstream issue...

      ...upgrade it to a new-style issue.

- 

      Simply mark it with an external-url field value.

      """

      log.info("    Upgrading %r %r issue for %r", downstream.key, issue.downstream, issue)
@@ -456,15 +450,13 @@ 

  def assign_user(client, issue, downstream, remove_all=False):

      """

      Attempts to assigns a JIRA issue to the correct

-     user based on the issue

-     Args:

-         client (jira.client.JIRA): JIRA Client

-         issue (sync2jira.intermediary.Issue): Issue object

-         downstream (jira.resources.Issue): JIRA issue object

-         remove_all (bool): Flag to indicate if we should reset

-                           the assignees in the JIRA issue

-     Returns:

-         Nothing

+     user based on the issue.

+ 

+     :param jira.client.JIRA client: JIRA Client

+     :param sync2jira.intermediary.Issue issue: Issue object

+     :param jira.resources.Issue downstream: JIRA issue object

+     :param Bool remove_all: Flag to indicate if we should reset the assignees in the JIRA issue

+     :returns: Nothing

      """

      # If removeAll flag, then we need to reset the assignees

      if remove_all:
@@ -500,14 +492,13 @@ 

  def _create_jira_issue(client, issue, config):

      """

      Create a JIRA issue and adds all relevant

-     information in the issue to the JIRA issue

-     Args:

-         client (jira.client.JIRA): JIRA client

-         issue (sync2jira.intermediary.Issue): Issue object

-         config (dict): Config dict

-     Returns:

-         downstream (jira.resources.Issue): Returns JIRA issue

-                                            that was created

+     information in the issue to the JIRA issue.

+ 

+     :param jira.client.JIRA client: JIRA client

+     :param sync2jira.intermediary.Issue issue: Issue object

+     :param Dict config: Config dict

+     :returns: Returns JIRA issue that was created

+     :rtype: jira.resources.Issue

      """

      log.info("   Creating %r issue for %r", issue.downstream, issue)

      if config['sync2jira']['testing']:
@@ -560,12 +551,12 @@ 

  def _label_matching(jira_labels, issue_labels):

      """

      Filters through jira_labels to ensure no duplicate labels are present and

-     no jira_labels are removed

-     Args:

-         jira_labels (lst(str)): Existing JIRA labels

-         issue_labels (lst(str): Upstream labels

-     Returns:

-         updated_labels (lst(str)): Updated filtered labels

+     no jira_labels are removed.

+ 

+     :param List jira_labels: Existing JIRA labels

+     :param List issue_labels: Upstream labels

+     :returns: Updated filtered labels

+     :rtype: List

      """

      # We want to get the union of the jira_labels and the issue_labels

      # i.e. all the labels in jira_labels and no duplicates from issue_labels
@@ -576,13 +567,12 @@ 

  

  def _update_jira_issue(existing, issue, client):

      """

-     Updates an existing JIRA issue (i.e. tags, assignee, comments etc)

-     Args:

-         existing (jira.resources.Issue): Existing JIRA issue that was found

-         issue (sync2jira.intermediary.Issue): Upstream issue we're pulling data from

-         client (jira.client.JIRA): JIRA Client

-     Returns:

-         Nothing

+     Updates an existing JIRA issue (i.e. tags, assignee, comments etc).

+ 

+     :param jira.resources.Issue existing: Existing JIRA issue that was found

+     :param sync2jira.intermediary.Issue issue: Upstream issue we're pulling data from

+     :param jira.client.JIRA client: JIRA Client

+     :returns: Nothing

      """

      # Start with comments

      # Only synchronize comments for listings that op-in
@@ -638,13 +628,12 @@ 

  

  def _update_transition(client, existing, issue):

      """

-     Helper function to update the transition of a downstream JIRA issue

-     Args:

-         client (jira.client.JIRA): JIRA client

-         existing (jira.resource.Issue): Existing JIRA issue

-         issue (sync2jira.intermediary.Issue): Upstream issue

-     Returns:

-         Nothing

+     Helper function to update the transition of a downstream JIRA issue.

+ 

+     :param jira.client.JIRA client: JIRA client

+     :param jira.resource.Issue existing: Existing JIRA issue

+     :param sync2jira.intermediary.Issue issue: Upstream issue

+     :returns: Nothing

      """

      # Update the issue status in the JIRA description

      # Format the status
@@ -702,12 +691,11 @@ 

  

  def _update_title(issue, existing):

      """

-     Helper function to sync upstream/downstream title

-     Args:

-         issue (sync2jira.intermediary.Issue): Upstream issue

-         existing (jira.resource.Issue): Existing JIRA issue

-     Returns:

-         Nothing

+     Helper function to sync upstream/downstream title.

+ 

+     :param sync2jira.intermediary.Issue issue: Upstream issue

+     :param jira.resource.Issue existing: Existing JIRA issue

+     :returns: Nothing

      """

      new_description = existing.fields.description

      if not new_description:
@@ -750,13 +738,12 @@ 

  

  def _update_comments(client, existing, issue):

      """

-     Helper function to sync comments between existing JIRA issue and upstream issue

-     Args:

-         client (jira.client.JIRA): JIRA client

-         existing (jira.resource.Issue): Existing JIRA issue

-         issue (sync2jira.intermediary.Issue): Upstream issue

-     Returns:

-         Nothing

+     Helper function to sync comments between existing JIRA issue and upstream issue.

+ 

+     :param jira.client.JIRA client: JIRA client

+     :param jira.resource.Issue existing: Existing JIRA issue

+     :param sync2jira.intermediary.Issue issue: Upstream issue

+     :returns: Nothing

      """

      # First get all existing comments

      comments = client.comments(existing)
@@ -773,13 +760,12 @@ 

  

  def _update_fixVersion(updates, existing, issue):

      """

-     Helper function to sync comments between existing JIRA issue and upstream issue

-     Args:

-         updates (list): Downstream updates requested by the user

-         existing (jira.resource.Issue): Existing JIRA issue

-         issue (sync2jira.intermediary.Issue): Upstream issue

-     Returns:

-         Nothing

+     Helper function to sync comments between existing JIRA issue and upstream issue.

+ 

+     :param List updates: Downstream updates requested by the user

+     :param jira.resource.Issue existing: Existing JIRA issue

+     :param sync2jira.intermediary.Issue issue: Upstream issue

+     :returns: Nothing

      """

      fix_version = []

      # If we are not supposed to overwrite JIRA content
@@ -827,14 +813,13 @@ 

  

  def _update_assignee(client, existing, issue, updates):

      """

-     Helper function update existing JIRA assignee from downstream issue

-     Args:

-         client (jira.client.JIRA): JIRA client

-         existing (jira.resource.Issue): Existing JIRA issue

-         issue (sync2jira.intermediary.Issue): Upstream issue

-         updates (list): Downstream updates requested by the user

-     Returns:

-         Nothing

+         Helper function update existing JIRA assignee from downstream issue.

+ 

+         :param jira.client.JIRA client: JIRA client

+         :param jira.resource.Issue existing: Existing JIRA issue

+         :param sync2jira.intermediary.Issue issue: Upstream issue

+         :param List updates: Downstream updates requested by the user

+         :returns: Nothing

      """

      # First check if overwrite is set to True

      try:
@@ -876,13 +861,12 @@ 

  

  def _update_tags(updates, existing, issue):

      """

-     Helper function to sync tags between upstream issue and downstream JIRA issue

-     Args:

-         updates (list): Downstream updates requested by the user

-         existing (jira.resource.Issue): Existing JIRA issue

-         issue (sync2jira.intermediary.Issue): Upstream issue

-     Returns:

-         Nothing

+     Helper function to sync tags between upstream issue and downstream JIRA issue.

+ 

+     :param List updates: Downstream updates requested by the user

+     :param jira.resource.Issue existing: Existing JIRA issue

+     :param sync2jira.intermediary.Issue issue: Upstream issue

+     :returns: Nothing

      """

      # First get all existing tags on the issue

      updated_labels = issue.tags
@@ -908,6 +892,13 @@ 

  

  

  def _update_description(existing, issue):

+     """

+     Helper function to sync description between upstream issue and downstream JIRA issue.

+ 

+     :param jira.resource.Issue existing: Existing JIRA issue

+     :param sync2jira.intermediary.Issue issue: Upstream issue

+     :returns: Nothing

+     """

      new_description = existing.fields.description

      if not new_description:

          new_description = ''
@@ -973,11 +964,11 @@ 

  

  def verify_tags(tags):

      """

-     Helper function to ensure tag are JIRA ready :)

-     Args:

-         tags (lst(str)): Input tags

-     Returns:

-         updated_tags (lst(str)): Updates tags

+     Helper function to ensure tag are JIRA ready :).

+ 

+     :param List tags: Input tags

+     :returns: Updates tags

+     :rtype: List

      """

      updated_tags = []

      for tag in tags:
@@ -988,12 +979,11 @@ 

  def sync_with_jira(issue, config):

      """

      Attempts to sync a upstream issue with JIRA (i.e. by finding

-     an existing issue or creating a new one)

-     Args:

-         issue (sync2jira.intermediary.Issue): Issue object

-         config (dict): Config dict

-     Returns:

-         Nothing

+     an existing issue or creating a new one).

+ 

+     :param sync2jira.intermediary.Issue issue: Issue object

+     :param Dict config: Config dict

+     :returns: Nothing

      """

  

      log.info("   Considering upstream %s, %s", issue.url, issue.title)
@@ -1035,6 +1025,15 @@ 

  

  

  def _close_as_duplicate(client, duplicate, keeper, config):

+     """

+     Helper function to close an issue as a duplicate.

+ 

+     :param jira.client client: JIRA Client

+     :param jira.resources.Issue duplicate: Duplicate JIRA Issue

+     :param jira.resources.Issue keeper: JIRA issue to keep

+     :param Dict config: Config dict

+     :returns: Nothing

+     """

      log.info("  Closing %s as duplicate of %s", duplicate.permalink(), keeper.permalink())

      if config['sync2jira']['testing']:

          log.info("      Testing flag is true.  Skipping actual delete.")
@@ -1079,6 +1078,13 @@ 

  

  

  def close_duplicates(issue, config):

+     """

+     Function to close duplicate JIRA issues.

+ 

+     :param sync2jira.intermediary.Issue issue: Upstream Issue

+     :param Dict config: Config dict

+     :returns: Nothing

+     """

      # Create a client connection for this issue

      client = _get_jira_client(issue, config)

  

file modified
+9 -6
@@ -13,12 +13,15 @@ 

  

  

  def send_mail(recipients, subject, text, cc):

-     """ Sends email to recipients

-     :param list recipients : recipients of email

-     :param string subject : subject of the email

-     :param string text: HTML text

-     :param: list cc : cc of the email

-     :pram string text: text of the email

+     """

+     Sends email to recipients.

+ 

+     :param List recipients: recipients of email

+     :param String subject: subject of the email

+     :param String text: HTML text

+     :param String cc: cc of the email

+     :param String text: text of the email

+     :returns: Nothing

      """

      _cfg = {}

      _cfg.setdefault("server", DEFAULT_SERVER)

file modified
+31 -26
@@ -66,13 +66,12 @@ 

  

  def load_config(loader=fedmsg.config.load_config):

      """

-     Generates and validates the config file

-     that will be used by fedmsg and JIRA client

-     Args:

-         loader (function): Function to set up runtime config

-     Returns:

-         config (dict): The config dict to be used

-                         later in the program

+     Generates and validates the config file \

+     that will be used by fedmsg and JIRA client.

+ 

+     :param Function loader: Function to set up runtime config

+     :returns: The config dict to be used later in the program

+     :rtype: Dict

      """

      config = loader()

  
@@ -110,13 +109,12 @@ 

  

  def listen(config):

      """

-     Listens to activity on upstream repos on pagure and github

-     via fedmsg, and syncs new issues there to the JIRA instance

+     Listens to activity on upstream repos on pagure and github \

+     via fedmsg, and syncs new issues there to the JIRA instance \

      defined in 'fedmsg.d/sync2jira.py'

-     Args:

-         config (dict): Config dict

-     Returns:

-         Nothing

+ 

+     :param Dict config: Config dict

+     :returns: Nothing

      """

      if not config['sync2jira'].get('listen'):

          log.info("`listen` is disabled.  Exiting.")
@@ -145,14 +143,13 @@ 

  

  def initialize(config):

      """

-     Initial initialization needed to sync any upstream

-     repo with JIRA. Goes through all issues and

-     checks if they're already on JIRA / Need to be

-     created

-     Args:

-         config (dict): Config dict for JIRA

-     Returns:

-         Nothing

+     Initial initialization needed to sync any upstream \

+     repo with JIRA. Goes through all issues and \

+     checks if they're already on JIRA / Need to be \

+     created.

+ 

+     :param Dict config: Config dict for JIRA

+     :returns: Nothing

      """

      log.info("   Running initialization to sync all issues from upstream to jira")

      log.info("   Testing flag is %r", config['sync2jira']['testing'])
@@ -179,11 +176,9 @@ 

  def main():

      """

      Main function to check for initial sync

-     and listen for fedmgs

-     Args:

-         Nothing

-     Returns:

-         Nothing

+     and listen for fedmgs.

+ 

+     :return: Nothing

      """

      config = load_config()

      logging.basicConfig(level=logging.INFO)
@@ -199,6 +194,11 @@ 

  

  

  def list_managed():

+     """

+     Function to list URL for issues under map in config.

+ 

+     :return: Nothing

+     """

      config = load_config()

      mapping = config['sync2jira']['map']

      warnings.simplefilter("ignore")
@@ -213,6 +213,11 @@ 

  

  

  def close_duplicates():

+     """

+     Function to close duplicate functions. Uses downstream:close_duplicates.

+ 

+     :return: Nothing

+     """

      config = load_config()

      logging.basicConfig(level=logging.INFO)

      log.info("   Testing flag is %r", config['sync2jira']['testing'])

file modified
+38 -2
@@ -37,6 +37,14 @@ 

  

  

  def handle_github_message(msg, config):

+     """

+     Handle GitHub message from FedMsg.

+ 

+     :param Dict msg: FedMsg Message

+     :param Dict config: Config File

+     :returns: Issue object

+     :rtype: sync2jira.intermediary.Issue

+     """

      owner = msg['msg']['repository']['owner']['login']

      repo = msg['msg']['repository']['name']

      upstream = '{owner}/{repo}'.format(owner=owner, repo=repo)
@@ -125,6 +133,14 @@ 

  

  

  def handle_pagure_message(msg, config):

+     """

+     Handle Pagure message from FedMsg.

+ 

+     :param Dict msg: FedMsg Message

+     :param Dict config: Config File

+     :returns: Issue object

+     :rtype: sync2jira.intermediary.Issue

+     """

      upstream = msg['msg']['project']['name']

      ns = msg['msg']['project'].get('namespace') or None

      if ns:
@@ -194,6 +210,14 @@ 

  

  

  def pagure_issues(upstream, config):

+     """

+     Creates a Generator for all Pagure issues in upstream repo.

+ 

+     :param String upstream: Upstream Repo

+     :param Dict config: Config Dict

+     :returns: Pagure Issue object generator

+     :rtype: sync2jira.intermediary.Issue

+     """

      base = config['sync2jira'].get('pagure_url', 'https://pagure.io')

      url = base + '/api/0/' + upstream + '/issues'

  
@@ -224,6 +248,14 @@ 

  

  

  def github_issues(upstream, config):

+     """

+     Creates a Generator for all GitHub issues in upstream repo.

+ 

+     :param String upstream: Upstream Repo

+     :param Dict config: Config Dict

+     :returns: Pagure Issue object generator

+     :rtype: sync2jira.intermediary.Issue

+     """

      token = config['sync2jira'].get('github_token')

      if not token:

          headers = {}
@@ -321,8 +353,9 @@ 

  

  

  def _github_link_field_to_dict(field):

-     """ Utility for ripping apart github's Link header field.

-     It's kind of ugly.

+     """

+         Utility for ripping apart github's Link header field.

+         It's kind of ugly.

      """

  

      if not field:
@@ -336,6 +369,9 @@ 

  

  

  def _fetch_github_data(url, headers):

+     """

+         Helper function to gather GitHub data

+     """

      response = requests.get(url, headers=headers)

      if not bool(response):

          try: