Skip to content

Instantly share code, notes, and snippets.

@paltman
Created May 3, 2017 11:06
Show Gist options
  • Save paltman/1ca16ca3e6d8e87ab8124bba8695db3b to your computer and use it in GitHub Desktop.
Save paltman/1ca16ca3e6d8e87ab8124bba8695db3b to your computer and use it in GitHub Desktop.
JIRA to ZenHub Migration Script
"""
1. Create a new repo and setup your pipeline to match JIRA
2. Edit the constants to suit your needs
3. Run `source jira.env`
4. Run `python jira-migration.py`
"""
import os
import requests
from jira import JIRA # pip install jira
from github import Github # pip install PyGithub
COMPONENT_LABEL_COLOR = "" # 6-digit hex color, e.g. 70B0A3
JIRA_PROJECT_KEY = "" # the prefix to your issues, e.g. KEY-1313
GITHUB_REPO = "" # org/repo
JIRA_URL = "" # https://YOURDOMAIN.atlassian.net
PEOPLE_MAP = {} # JIRA username to Github username mapping, e.g. { "jirausername": "githubusername" }
ZENHUB_BASE_URL = "https://api.zenhub.io/p1/repositories/"
COMPONENTS = set()
ZENHUB_HEADERS = {
"X-Authentication-Token": os.environ["ZENHUB_TOKEN"]
}
def get_github_repo():
g = Github(os.environ["GITHUB_TOKEN"])
return g.get_repo(GITHUB_REPO)
def get_zenhub_board(repo_id):
return requests.get(
"https://api.zenhub.io/p1/repositories/{}/board".format(repo_id),
headers=ZENHUB_HEADERS
).json()
def get_zenhub_pipelines(repo_id):
board = get_zenhub_board(repo_id)
return {
p["name"]: p["id"]
for p in board["pipelines"]
}
def get_jira():
return JIRA(JIRA_URL, basic_auth=(
os.environ["JIRA_USER"],
os.environ["JIRA_PASS"]
))
def get_epics(jira):
query = "project={} and type=\"Epic\"".format(JIRA_PROJECT_KEY)
return {
i.key: {"epic_issue": None, "issues": []}
for i in jira.search_issues(query, maxResults=800)
}
def get_issues(jira):
query = """
project={}
and status != "Closed"
and status != "Done"
and status != "Accepted"
and status != "Resolved"
""".format(JIRA_PROJECT_KEY)
return jira.search_issues(query, maxResults=800, json_result=True)
def zh_move_issue_to_pipeline(repo_id, issue_number, pipeline_id):
url = "{}{}/issues/{}/moves".format(ZENHUB_BASE_URL, repo_id, issue_number)
if pipeline_id:
requests.post(
url,
data={"pipeline_id": pipeline_id, "position": "top"},
headers=ZENHUB_HEADERS
)
def zh_set_estimate(repo_id, issue_number, estimate):
url = "{}{}/issues/{}/estimate".format(ZENHUB_BASE_URL, repo_id, issue_number) # noqa
if estimate:
requests.put(url, data={"estimate": estimate}, headers=ZENHUB_HEADERS)
def zh_convert_to_epic(repo_id, issue_number):
url = "{}{}/issues/{}/convert_to_epic".format(ZENHUB_BASE_URL, repo_id, issue_number) # noqa
requests.post(url, headers=ZENHUB_HEADERS)
def zh_set_epics(repo_id, epics):
for epic in epics.keys():
url = "{}{}/epics/{}/update_issues".format(ZENHUB_BASE_URL, repo_id, epics[epic]["epic_issue"].number) # noqa
response = requests.post(url, json={
"add_issues": [
{"repo_id": repo_id, "issue_number": issue.number}
for issue in epics[epic]["issues"]
]
}, headers=ZENHUB_HEADERS)
print(response.status_code, response.json())
def gh_set_labels_color(repo):
"""
The only labels so far in the fresh repo are ones created via components.
Get all labels. Set them all to COMPONENT_LABEL_COLOR.
"""
for label in COMPONENTS:
l = repo.get_label(label)
l.edit(l.name, COMPONENT_LABEL_COLOR)
def migrate_issue(jira_issue, repo, pipelines):
jira_id = jira_issue["key"]
pipeline = jira_issue["fields"]["status"]["name"]
pipeline_id = pipelines.get(pipeline)
title = jira_issue["fields"]["summary"]
if jira_issue["fields"]["description"]:
description = jira_issue["fields"]["description"].encode("utf-8")
else:
description = ""
body = "{}\n\n[{}]({}/browse/{})".format(
description,
jira_id,
JIRA_URL,
jira_id
)
estimate = jira_issue["fields"].get("customfield_10021")
jira_assignee = jira_issue["fields"].get("assignee")
if jira_assignee is not None:
assigned = PEOPLE_MAP.get(jira_assignee.get("key"))
else:
assigned = None
labels = [
component["name"]
for component in
jira_issue["fields"].get("components", [])
]
COMPONENTS.update(labels)
# 1. Create the Issue
i = repo.create_issue(
title=title,
body=body,
assignees=[assigned] if assigned is not None else [],
labels=labels
)
# 2. Move to right Pipeline
zh_move_issue_to_pipeline(repo.id, i.number, pipeline_id)
# 3. Set any existing estimate
zh_set_estimate(repo.id, i.number, estimate) # noqa
return i
def migrate_issues():
repo = get_github_repo()
pipelines = get_zenhub_pipelines(repo.id)
jira = get_jira()
epics = get_epics(jira)
issues = get_issues(jira)["issues"]
for issue in issues:
gh_issue = migrate_issue(issue, repo, pipelines)
epic_id = issue["fields"].get("customfield_10017")
jira_id = issue["key"]
# 4a. Collect Epic information
if epics.get(jira_id):
epics[jira_id]["epic_issue"] = gh_issue
zh_convert_to_epic(repo.id, gh_issue.number)
if epic_id and epics.get(epic_id):
epics[epic_id]["issues"].append(gh_issue)
print("Created Issue #{} - {}".format(gh_issue.number, jira_id))
# 4b. Set Epics
zh_set_epics(repo.id, epics)
# Set label color
gh_set_labels_color(repo)
if __name__ == "__main__":
migrate_issues()
export ZENHUB_TOKEN=
export GITHUB_TOKEN=
export JIRA_USER=
export JIRA_PASS=
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment