Skip to content

Commit

Permalink
Sync priority from GitHub to Jira (#216)
Browse files Browse the repository at this point in the history
Fixes #198

Example configuration:
```
"default_jira_fields": {
  "storypoints": "customfield_12310243"
},

"map": {
  "github": {
    "developerproductivity/open-connectors": {
      "project": "TEST",
      "sync": [ "issue" ],
      "issue_updates": [ "github_project_fields" ],
      "github_project_number": 1,
      "github_project_fields": {
        "storypoints": {
          "gh_field": "Estimate"
        },
        "priority": {
          "gh_field": "Priority",
          "options": {
            "P0": "Blocker",
            "P1": "Critical",
            "P2": "Major",
            "P3": "Minor",
            "P4": "Optional",
            "P5": "Trivial"
          }
        }
      }
    }
  }
},
```

Co-authored-by: Ralph Bean <[email protected]>
  • Loading branch information
baijum and ralphbean authored Nov 11, 2024
1 parent 61b2748 commit a4a2bac
Show file tree
Hide file tree
Showing 10 changed files with 525 additions and 65 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ fedmsg.d/sync2jira.py
/.tox
*.pyc
__pycache__
.vscode/settings.json
47 changes: 47 additions & 0 deletions docs/source/config-file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,22 @@ The config file is made up of multiple parts
* Here you can configure multiple JIRA instances if you have projects with differing downstream JIRA instances.
Ensure to name them appropriately, in name of the JIRA instance above is `example`.

.. code-block:: python
"default_jira_fields": {
"storypoints": "customfield_12310243"
},
* The keys in the :code:`default_jira_fields` map are used as the values of the github_project_fields
list (see below). The keys indicate how the fields in GitHub Project items correspond to
fields in Jira issues so that these values can be synced properly. Currently, we support
only :code:`storypoints` and :code:`priority`. By default, the value for the priority is sourced
from the upstream :code:`priority` field, but providing a :code:`priority` key for
:code:`default_jira_fields` will override that. Unfortunately, there is no default available
for sourcing story points, so, the :code:`storypoints` key must be provided if projects wish
to sync story point values, and, for our corporate Jira instance, the value should be specified
as :code:`customfield_12310243`.

.. code-block:: python
'map': {
Expand Down Expand Up @@ -124,6 +140,10 @@ The config file is made up of multiple parts
* If selected this will add a comment to all newly created JIRA issue in the format 'UPSTREAM_PROJECT-#1' where the number indicates the issue ID. This allows users to search for the issue on JIRA via the issue number.
* :code:`url`
* This flag will add the upstream url to the bottom of the JIRA ticket
* :code:`github_project_number`
* Specify the GitHub project number. If specified, story points and priority will be selected from this project, and other projects linked to the issues will be ignored.
* :code:`github_project_fields`
* Sync GitHub projects fields. e.g, storypoints, priority

.. note::

Expand All @@ -146,6 +166,33 @@ The config file is made up of multiple parts
* It is strongly encouraged for teams to use the :code:`owner` field. If configured, owners will be alerted if Sync2Jira finds duplicate 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
'github_project_fields': {
'storypoints': {
'gh_field': 'Estimate'
},
'priority': {
'gh_field': 'Priority',
'options': {
'P0': 'Blocker',
'P1': 'Critical',
'P2': 'Major',
'P3': 'Minor',
'P4': 'Optional',
'P5': 'Trivial'
}
}
}
* Specify the GitHub project field and its mapping to Jira. The currently supportted fields are :code:`storypoints`
and :code:`priority`.

* The :code:`gh_field` points to the GitHub field name.

* The :code:`options` key is used to specify a mapping between GitHub field values as Jira field values.
This is particularly useful for cases like priority which uses keywords for values.

.. code-block:: python
'filters': {
Expand Down
4 changes: 2 additions & 2 deletions docs/source/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ Want to quickly get started working with Sync2Jira? Follow these steps:
'map': {
'github': {
'GITHUB_USERNAME/Demo_project': {'project': 'FACTORY', 'component': 'gitbz',
'updates': [...], 'sync': [..]},
'issue_updates': [...], 'sync': [..]},
},
},
.. note:: You can learn more about what can go into the updates list `here <config-file.html>`_
.. note:: You can learn more about what can go into the issue_updates list `here <config-file.html>`_

5. Finally you can tweak the config files optional settings to your liking
.. code-block:: python
Expand Down
22 changes: 20 additions & 2 deletions fedmsg.d/sync2jira.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,29 @@
'token_auth': 'YOUR_JIRA_ACCESS_TOKEN',
},
},
'default_github_project_fields': {'storypoints': ('Estimate', 'customfield_12310243')},
'default_jira_fields': {
'storypoints': 'customfield_12310243',
},
'map': {
'github': {
'GITHUB_USERNAME/Demo_project': {'project': 'FACTORY', 'component': 'gitbz',
'updates': [...], 'sync': ['pullrequest', 'issue']},
'issue_updates': [
'comments',
'upstream_id',
'title',
'description',
'github_markdown',
'upstream_id',
'url',
{'transition': 'Closed'},
{'assignee': {'overwrite': False}},
'github_project_fields'],
'github_project_number': '1',
'github_project_fields': {'storypoints': {'gh_field': 'Estimate'},
'priority': {'gh_field': 'Priority', 'options':
{'P0': 'Blocker', 'P1': 'Critical', 'P2': 'Major',
'P3': 'Minor', 'P4': 'Optional', 'P5': 'Trivial'}}},
'sync': ['pullrequest', 'issue']},
},
},
'filters': {
Expand Down
43 changes: 32 additions & 11 deletions sync2jira/downstream_issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ def _create_jira_issue(client, issue, config):

# Update relevant information (i.e. tags, assignees etc.) if the
# User opted in
_update_jira_issue(downstream, issue, client)
_update_jira_issue(downstream, issue, client, config)

return downstream

Expand All @@ -725,7 +725,7 @@ def _label_matching(jira_labels, issue_labels):
return updated_labels


def _update_jira_issue(existing, issue, client):
def _update_jira_issue(existing, issue, client, config):
"""
Updates an existing JIRA issue (i.e. tags, assignee, comments etc).
Expand All @@ -751,7 +751,8 @@ def _update_jira_issue(existing, issue, client):
# Only synchronize comments for listings that op-in
if 'github_project_fields' in updates and len(github_project_fields) > 0:
log.info("Looking for GitHub project fields")
_update_github_project_fields(client, existing, issue, github_project_fields)
_update_github_project_fields(client, existing, issue,
github_project_fields, config)

# Only synchronize comments for listings that op-in
if 'comments' in updates:
Expand Down Expand Up @@ -967,7 +968,8 @@ def _update_jira_labels(issue, labels):
log.info('Updated %s tag(s)' % len(_labels))


def _update_github_project_fields(client, existing, issue, github_project_fields):
def _update_github_project_fields(client, existing, issue,
github_project_fields, config):
"""Update a Jira issue with GitHub project item field values
:param jira.client.JIRA client: JIRA client
Expand All @@ -976,13 +978,32 @@ def _update_github_project_fields(client, existing, issue, github_project_fields
:param list: Fields representing GitHub project item fields in GitHub and Jira
"""

default_jira_fields = config['sync2jira'].get('default_jira_fields', {})
for name, values in github_project_fields.items():
_, jirafieldname = values
try:
existing.update({jirafieldname: str(getattr(issue, name))})
except JIRAError as err:
# Add a comment to indicate there was an issue
client.add_comment(existing, f"Error updating GitHub project field: {err}")
fieldvalue = getattr(issue, name)
if name == 'storypoints':
try:
jirafieldname = default_jira_fields['storypoints']
except KeyError:
log.error("Configuration error: Missing 'storypoints' in `default_jira_fields`")
continue
try:
existing.update({jirafieldname: fieldvalue})
except JIRAError as err:
# Add a comment to indicate there was an issue
client.add_comment(existing, f"Error updating GitHub project storypoints field: {err}")
elif name == 'priority':
try:
jirafieldname = default_jira_fields['priority']
except KeyError:
jirafieldname = 'priority'
jira_priority = values.get('options', {}).get(fieldvalue)
if jira_priority:
try:
existing.update({jirafieldname: {'name': jira_priority}})
except JIRAError as err:
# Add a comment to indicate there was an issue
client.add_comment(existing, f"Error updating GitHub project priority field: {err}")


def _update_tags(updates, existing, issue):
Expand Down Expand Up @@ -1157,7 +1178,7 @@ def sync_with_jira(issue, config):
log.info("Testing flag is true. Skipping actual update.")
return
# Update relevant metadata (i.e. tags, assignee, etc)
_update_jira_issue(existing, issue, client)
_update_jira_issue(existing, issue, client, config)
return

# If we're *not* configured to do legacy matching (upgrade mode) then there
Expand Down
3 changes: 1 addition & 2 deletions sync2jira/intermediary.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ def from_github(cls, upstream, issue, config):
if any('fixVersion' in item for item in mapping):
map_fixVersion(mapping, issue)

# TODO: Priority is broken
return cls(
source=upstream_source,
title=issue['title'],
Expand All @@ -103,7 +102,7 @@ def from_github(cls, upstream, issue, config):
comments=comments,
tags=issue['labels'],
fixVersion=[issue['milestone']],
priority=None,
priority=issue['priority'],
content=issue['body'] or '',
reporter=issue['user'],
assignee=issue['assignees'],
Expand Down
Loading

0 comments on commit a4a2bac

Please sign in to comment.