Skip to content

Instantly share code, notes, and snippets.

@yoshimaru46
Last active November 24, 2020 06:37
Show Gist options
  • Save yoshimaru46/1b60276dd24a4fedbaf1fe980faca169 to your computer and use it in GitHub Desktop.
Save yoshimaru46/1b60276dd24a4fedbaf1fe980faca169 to your computer and use it in GitHub Desktop.
JIRA to Github Issues Migration Script

Refs:

require 'nokogiri'
require 'reverse_markdown'
require 'octokit'

GITHUB_ACCESS_TOKEN = 'XXXX'
ORG = 'XXXX'
REPO = 'XXXX'

# Debug Mode
debug = true

file = File.open("jira-export/XXXX.xml")

# jira to github
label_mapping = [
  {
    jira: 'きのこ',
    github: 'きのこ 🍄'
  },
  {
    jira: 'たけのこ',
    github: 'たけのこ🎍'
  },
  {
    jira: 'Design',
    github: 'Design'
  },
  {
    jira: 'Backend',
    github: 'Backend'
  },
  {
    jira: 'Frontend',
    github: 'Frontend'
  },
  {
    jira: 'FrontEnd',
    github: 'Frontend'
  },
  {
    jira: '事前案内不要',
    github: 'リリース案内: 不要'
  },
  {
    jira: 'リリース当日案内',
    github: 'リリース案内: 当日'
  },
  {
    jira: 'リリース前日案内',
    github: 'リリース案内: 前日'
  },
  {
    jira: 'リリース3日前案内',
    github: 'リリース案内: 3日前'
  },
  {
    jira: 'リリース1週間前案内',
    github: 'リリース案内: 1週間前'
  },
]


begin
  entities = Nokogiri::XML::Document.parse(file, nil, "UTF-8") do |config|
    config.norecover
    config.nonoent
    config.huge
  end
rescue Nokogiri::XML::SyntaxError => e
  p e.code
  p e.column
  p e.message
  raise e
end

issues = debug ? entities.xpath('rss/channel/item').take(10) : entities.xpath('rss/channel/item')

p "Total Issues size"
p issues.size

new_issue_list = []

issues.each do |issue|
  new_issue = {}
  new_issue[:title] = issue.xpath(".//title").text
  new_issue[:link] = issue.xpath(".//link").text
  new_issue[:type] = issue.xpath(".//type").text
  new_issue[:summary] = issue.xpath(".//summary").text
  new_issue[:status] = issue.xpath(".//status").text
  new_issue[:statusCategory] = issue.xpath(".//statusCategory").text
  new_issue[:resolution] = issue.xpath(".//resolution").text
  new_issue[:assignee] = issue.xpath(".//assignee").text
  new_issue[:reporter] = issue.xpath(".//reporter").text
  labels_node = issue.xpath(".//labels")
  new_issue[:labels] = labels_node.map { |l| l.xpath(".//label").map(&:text) }.flatten
  new_issue[:created] = issue.xpath(".//created").text
  new_issue[:updated] = issue.xpath(".//updated").text
  new_issue[:due] = issue.xpath(".//due").text
  new_issue[:priority] = issue.xpath(".//priority").text
  new_issue[:description] = issue.xpath(".//description").text

  # customfields = issue.xpath(".//customfields")
  #
  # sprint = customfields.xpath(".//customfield[@id='customfield_10021']")
  # sprint_text = sprint.xpath('.//customfieldvalues').map do |value|
  #   value.xpath('.//customfieldvalue').map(&:text)
  # end.flatten
  # new_issue[:sprint] = sprint_text
  #
  # story_point = customfields.xpath(".//customfield[@id='customfield_10016']")
  # story_point_text = story_point.xpath('.//customfieldvalues').map do |value|
  #   value.xpath('.//customfieldvalue').map(&:text)
  # end.last&.last
  # new_issue[:story_point] = story_point_text
  #
  # pre_announcement = customfields.xpath(".//customfield[@id='customfield_10054']")
  # pre_announcement_text = pre_announcement.xpath('.//customfieldvalues').map do |value|
  #   value.xpath('.//customfieldvalue').map(&:text)
  # end.last&.last

  # new_issue[:labels] = new_issue[:labels].push(pre_announcement_text)

  target_status_list = ['Sprint Backlog', 'In Progress', 'QA待ち&QA中']
  next unless target_status_list.include? new_issue[:status]

  new_issue_list << new_issue
end

p "Target Issues size"
p new_issue_list.size

client = Octokit::Client.new(:access_token => GITHUB_ACCESS_TOKEN)

# Githubにissueを作成する
new_issue_list.each do |item|
  body = []
  body << ":memo: JIRAからマイグレーションされたissueです"
  body << ""
  p item[:link]
  body << "JIRA URL: #{item[:link]}"
  body << "type: #{item[:type]}"
  body << "summary: #{item[:summary]}"
  body << "status: #{item[:status]}"
  body << "statusCategory: #{item[:statusCategory]}"
  body << "resolution: #{item[:resolution]}"
  body << "assignee: #{item[:assignee]}"
  body << "reporter: #{item[:reporter]}"
  body << "created: #{item[:created]}"
  body << "updated: #{item[:updated]}"
  body << "due: #{item[:due]}"
  body << "priority: #{item[:priority]}"
  # body << "sprint: #{item[:sprint].join(',')}"
  # body << "story_point: #{item[:story_point]}"
  body << ""
  body << "---"
  body << item[:description]

  body_text = ReverseMarkdown.convert(body.join(("<br>")), github_flavored: true)

  github_labels = item[:labels].map do |label|
    mapping = label_mapping.find { |mapping| mapping[:jira] == label }
    mapping ? mapping[:github] : nil
  end.compact
  options = {}

  options[:labels] = github_labels

  client.create_issue("#{ORG}/#{REPO}", item[:title], body_text, options)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment