From b3ee9a98db27c118f92275b1607a34c962ab2f40 Mon Sep 17 00:00:00 2001 From: Jesper Grann Laursen Date: Tue, 22 Sep 2015 23:56:09 +0200 Subject: [PATCH 1/7] Updated the readme and minor changes to the configuration example --- README.md | 16 +++++++++++++++- migrate_jira.rake | 9 ++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1483d03..f614065 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,23 @@ for more information please look at: http://www.redmine.org/issues/1385 ## How to -Copy `migrate_jira.rake` to `lib/tasks` in your *Redmine* directory and the execute the following: +Copy `migrate_jira.rake` to `lib/tasks` in your *Redmine* directory + +Unzip the Jira backup file into `tmp` + +Change the variables in top of the file to reflect the backup files + + ENTITIES_FILE = 'tmp/JIRA-backup-XXXXXXXX/entities.xml' + JIRA_ATTACHMENTS_DIR = 'tmp/JIRA-backup-XXXXXXXX/data/attachments' + $JIRA_WEB_URL = 'https://.atlassian.net' + +and the execute the following: ``` rake jira_migration:test_all_migrations RAILS_ENV="production" rake jira_migration:do_all_migrations RAILS_ENV="production" ``` + +On first `test_all_migrations` a `map_jira_to_redmine.yml` is created and needs to be updated, before running the next migration command (or the test again) + +If a user is created by the migration, the default password will be `Pa$$w0rd`, and needs to be changed on first login. diff --git a/migrate_jira.rake b/migrate_jira.rake index f3c17b3..a3c81cc 100644 --- a/migrate_jira.rake +++ b/migrate_jira.rake @@ -13,13 +13,12 @@ module JiraMigration ############## Configuration mapping file. Maps Jira Entities to Redmine Entities. Generated on the first run. CONF_FILE = 'map_jira_to_redmine.yml' ############## Jira backup main xml file with all data - ENTITIES_FILE = 'entities.xml' + ENTITIES_FILE = 'tmp/JIRA-backup-20150922/entities.xml' ############## Location of jira attachements - JIRA_ATTACHMENTS_DIR = 'attachments' - #JIRA_ATTACHMENTS_DIR = '/home/ubuntu/JIRA-backup-20150303/data/attachments' + JIRA_ATTACHMENTS_DIR = 'tmp/JIRA-backup-20150922/data/attachments' ############## Jira URL - $JIRA_WEB_URL = 'https://glorium.jira.com' - #$JIRA_WEB_URL = 'https://leasepipeline.atlassian.net' + $JIRA_WEB_URL = 'https://companyurl.atlassian.net' + class BaseJira From f30cf5ef129210d30c7c1e3b7a820162ead06b8e Mon Sep 17 00:00:00 2001 From: Jesper Grann Laursen Date: Tue, 22 Sep 2015 23:57:34 +0200 Subject: [PATCH 2/7] Remote the Jira ID from issue comments and fixed to all comments are migrated --- migrate_jira.rake | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/migrate_jira.rake b/migrate_jira.rake index a3c81cc..0c394ea 100644 --- a/migrate_jira.rake +++ b/migrate_jira.rake @@ -333,9 +333,9 @@ module JiraMigration def initialize(node) super # get a body from a comment - # comment can have the comment body as a attribute or as a child tag - #@jira_body = @tag["body"] || @tag.at("body").text - @jira_body = node['body'] + # comment can have the comment body as a attribute or as a child tag + @jira_body = @tag["body"] || @tag.at("body").text + #@jira_body = node['body'] end def jira_marker @@ -347,8 +347,9 @@ module JiraMigration # here is the tranformation of Jira attributes in Redmine attribues def red_notes + # You can have the marker in the notes, if you want. Else only the body will be migrated #self.jira_marker + "\n" + @jira_body - "#{self.jira_marker}\n%s" % @jira_body + @jira_body end def red_created_on DateTime.parse(self.jira_created) From b6128827d82c583d75693441c3e850753163ba75 Mon Sep 17 00:00:00 2001 From: Jesper Grann Laursen Date: Wed, 23 Sep 2015 00:06:16 +0200 Subject: [PATCH 3/7] Added a test_parse_attachments task --- migrate_jira.rake | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/migrate_jira.rake b/migrate_jira.rake index 0c394ea..6aa6c21 100644 --- a/migrate_jira.rake +++ b/migrate_jira.rake @@ -1036,13 +1036,20 @@ namespace :jira_migration do issues.each {|i| pp( i.run_all_redmine_fields) } end + desc "Just pretty print Jira Attachments on screen" + task :test_parse_attachments => :environment do + attachments = JiraMigration.parse_attachments() + attachments.each {|i| pp( i.run_all_redmine_fields) } + end + ##################################### Running all tests ########################################## desc "Tests all parsers!" task :test_all_migrations => [:environment, :pre_conf, :test_parse_projects, :test_parse_users, :test_parse_comments, - :test_parse_issues] do + :test_parse_issues, + :test_parse_attachments] do puts "All parsers was run! :-)" end From 149965ce5bd1144cba9510c3198aa57681f8ce12 Mon Sep 17 00:00:00 2001 From: Jesper Grann Laursen Date: Wed, 23 Sep 2015 00:08:13 +0200 Subject: [PATCH 4/7] Fixed the import of attachments, there the structure is like attachments/// --- migrate_jira.rake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/migrate_jira.rake b/migrate_jira.rake index 6aa6c21..f89c040 100644 --- a/migrate_jira.rake +++ b/migrate_jira.rake @@ -382,13 +382,15 @@ module JiraMigration pp(new_record) # JIRA stores attachments as follows: - # //_filename.ext + # //_filename.ext # # We have to recreate this path in order to copy the file issue_key = $MAP_ISSUE_TO_PROJECT_KEY[self.jira_issue][:issue_key] project_key = $MAP_ISSUE_TO_PROJECT_KEY[self.jira_issue][:project_key] + project_id = $MAP_ISSUE_TO_PROJECT_KEY[self.jira_issue][:project_id] jira_attachment_file = File.join(JIRA_ATTACHMENTS_DIR, project_key, + project_id, issue_key, "#{self.jira_id}") puts "Jira Attachment File: #{jira_attachment_file}" @@ -832,7 +834,7 @@ namespace :jira_migration do #$doc.elements.each("/*/Issue") do |i| $doc.xpath("/*/Issue").each do |i| - $MAP_ISSUE_TO_PROJECT_KEY[i["id"]] = { :project_key => $MAP_PROJECT_ID_TO_PROJECT_KEY[i["project"]], :issue_key => i['key']} + $MAP_ISSUE_TO_PROJECT_KEY[i["id"]] = { :project_id => i["project"], :project_key => $MAP_PROJECT_ID_TO_PROJECT_KEY[i["project"]], :issue_key => i['key']} end end From b32cbe8b7747d41deccb0e965b0e4e1ba5219b47 Mon Sep 17 00:00:00 2001 From: Jesper Grann Laursen Date: Wed, 23 Sep 2015 15:35:19 +0200 Subject: [PATCH 5/7] Update README.md Minor formating change --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f614065..ccfc00f 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,11 @@ Unzip the Jira backup file into `tmp` Change the variables in top of the file to reflect the backup files +``` ENTITIES_FILE = 'tmp/JIRA-backup-XXXXXXXX/entities.xml' JIRA_ATTACHMENTS_DIR = 'tmp/JIRA-backup-XXXXXXXX/data/attachments' $JIRA_WEB_URL = 'https://.atlassian.net' +``` and the execute the following: From f0aafbbc7874a2b683ad5e932e29dd5f30f8dfc4 Mon Sep 17 00:00:00 2001 From: Amr Date: Sat, 21 May 2016 01:55:13 +0200 Subject: [PATCH 6/7] ability to read the 'sprints' custom field and ammend them to the description --- migrate_jira.rake | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/migrate_jira.rake b/migrate_jira.rake index f89c040..be84832 100644 --- a/migrate_jira.rake +++ b/migrate_jira.rake @@ -13,11 +13,11 @@ module JiraMigration ############## Configuration mapping file. Maps Jira Entities to Redmine Entities. Generated on the first run. CONF_FILE = 'map_jira_to_redmine.yml' ############## Jira backup main xml file with all data - ENTITIES_FILE = 'tmp/JIRA-backup-20150922/entities.xml' + ENTITIES_FILE = 'entities.xml' ############## Location of jira attachements - JIRA_ATTACHMENTS_DIR = 'tmp/JIRA-backup-20150922/data/attachments' + JIRA_ATTACHMENTS_DIR = 'data/attachments' ############## Jira URL - $JIRA_WEB_URL = 'https://companyurl.atlassian.net' + $JIRA_WEB_URL = 'https://unitedofoq.atlassian.net' class BaseJira @@ -72,11 +72,15 @@ module JiraMigration record.save! record.reload + puts "after save" + self.map[self.jira_id] = record self.new_record = record if self.respond_to?('post_migrate') self.post_migrate(record, self.is_new) end + puts "after migrate" + record.reload return record end @@ -255,7 +259,7 @@ module JiraMigration JiraProject::MAP[self.jira_project] end -=begin +begin def red_fixed_version path = "/*/NodeAssociation[@sourceNodeId=\"#{self.jira_id}\" and @sourceNodeEntity=\"Issue\" and @sinkNodeEntity=\"Version\" and @associationType=\"IssueFixVersion\"]" assocs = JiraMigration.get_list_from_tag(path) @@ -266,7 +270,8 @@ module JiraMigration end versions.last end -=end +end + def red_subject self.jira_summary @@ -576,7 +581,6 @@ module JiraMigration nm = node.attr("name") ret.push(Hash[node.attributes.map { |k,v| [k,v.content]}])} #ret.push(node.attributes.rehash)} - return ret end @@ -642,8 +646,14 @@ module JiraMigration issue_from.reload else r = IssueRelation.new(:relation_type => linktype, :issue_from => issue_from, :issue_to => issue_to) - r.save! - r.reload + puts "setting relation between: #{issue_from.id} to: #{issue_to.id}" + begin + r.save! + r.reload + rescue Exception => e + puts "FAILED setting #{linktype} relation from: #{issue_from.id} to: #{issue_to.id} because of #{e.message}" + end + puts "After saving the relation" end end end @@ -757,10 +767,13 @@ module JiraMigration # the key will be the identifier projs = [] # $doc.elements.each('/*/Project') do |node| + puts "before" $doc.xpath('/*/Project').each do |node| proj = JiraProject.new(node) projs.push(proj) end + puts "after" + puts projs.length() migrated_projects = {} projs.each do |p| @@ -920,6 +933,8 @@ namespace :jira_migration do if t.nil? Tracker.new(name: value) end + puts "key: " + key + puts "value: " + value t.save! t.reload $MIGRATED_ISSUE_TYPES[key] = t @@ -936,6 +951,8 @@ namespace :jira_migration do if s.nil? s = IssueStatus.new(name: value) end + puts "key: " + key + puts "value: " + value s.save! s.reload $MIGRATED_ISSUE_STATUS[key] = s @@ -953,6 +970,8 @@ namespace :jira_migration do if p.nil? p = IssuePriority.new(name: value) end + puts "key: " + key + puts "value: " + value p.save! p.reload $MIGRATED_ISSUE_PRIORITIES[key] = p @@ -1016,7 +1035,9 @@ namespace :jira_migration do ##################################### Tests ########################################## desc "Just pretty print Jira Projects on screen" task :test_parse_projects => :environment do + puts "before parse projects" projects = JiraMigration.parse_projects() + puts "these are the projects: " projects.each {|p| pp(p.run_all_redmine_fields) } end From 513801e0784ee9bb11b76bd88002a3bfe7bf5d1c Mon Sep 17 00:00:00 2001 From: Amr Noaman Abdel-Hamid Date: Sun, 29 May 2016 11:33:21 +0200 Subject: [PATCH 7/7] migrate inactive jira users into locked redmine users, instead of replacing them with deleted users --- migrate_jira.rake | 105 +++++++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 33 deletions(-) diff --git a/migrate_jira.rake b/migrate_jira.rake index be84832..e44da97 100644 --- a/migrate_jira.rake +++ b/migrate_jira.rake @@ -17,7 +17,7 @@ module JiraMigration ############## Location of jira attachements JIRA_ATTACHMENTS_DIR = 'data/attachments' ############## Jira URL - $JIRA_WEB_URL = 'https://unitedofoq.atlassian.net' + $JIRA_WEB_URL = 'https://company.atlassian.net' class BaseJira @@ -72,14 +72,12 @@ module JiraMigration record.save! record.reload - puts "after save" - + self.map[self.jira_id] = record self.new_record = record if self.respond_to?('post_migrate') self.post_migrate(record, self.is_new) end - puts "after migrate" record.reload return record @@ -129,6 +127,13 @@ module JiraMigration def red_login self.jira_name end + def red_status + if (self.jira_active.to_s == '1') + return 1 + else + return 3 #locked user + end + end def before_save(new_record) new_record.login = red_login if new_record.new_record? @@ -259,7 +264,6 @@ module JiraMigration JiraProject::MAP[self.jira_project] end -begin def red_fixed_version path = "/*/NodeAssociation[@sourceNodeId=\"#{self.jira_id}\" and @sourceNodeEntity=\"Issue\" and @sinkNodeEntity=\"Version\" and @associationType=\"IssueFixVersion\"]" assocs = JiraMigration.get_list_from_tag(path) @@ -270,14 +274,63 @@ begin end versions.last end -end - + + def get_change_groups_of_issue(issueId) + path = "/*/ChangeGroup[@issue=\"#{issueId}\"]/@id" + groupIdAtts = $doc.xpath(path) + groupIds = [] + groupIdAtts.each do |item| + groupIds.push(item.content) + end + return groupIds + end + + def get_sprint_changes_of_specific_group(groupId) + path = "/*/ChangeItem[@group=\"#{groupId}\" and @fieldtype=\"custom\" and @field=\"Sprint\"]/@newstring" + sprintAttributes = $doc.xpath(path) + sprints = [] + latestSprint = "" + sprintAttributes.each do |item| + if (item.content.length != 0) + latestSprint = item.content + end + end + + return latestSprint + end + + def get_previous_sprints + groupIds = get_change_groups_of_issue(self.jira_id) + sprints = [] + selected = "" + currentSprint = "" + groupIds.each do |item| + if (item != nil and item.to_s != '') + currentSprint = get_sprint_changes_of_specific_group(item) + if (currentSprint.length !=0) + selected = currentSprint + end + sprints.push(currentSprint) + end + end + + #puts("This is the jira id: #{self.jira_id} and this is the selected sprint: #{selected} out of: #{sprints}") + + return selected + end def red_subject self.jira_summary end + def red_description - "#{self.jira_marker}\n%s" % @jira_description + sprints = get_previous_sprints + if (sprints.to_s == '') + dsc = "#{self.jira_marker}\n%s" % @jira_description + else + dsc = "#{self.jira_marker}\n Sprints: #{get_previous_sprints}\n\n%s" % @jira_description + end + return dsc #dsc = self.jira_marker + "\n" #if @jira_description # dsc += @jira_description @@ -286,6 +339,7 @@ end #end #return dsc end + def red_priority name = $MIGRATED_ISSUE_PRIORITIES_BY_ID[self.jira_priority] return $MIGRATED_ISSUE_PRIORITIES[name] @@ -723,20 +777,13 @@ end # $doc.elements.each('/*/User') do |node| $doc.xpath('/*/User').each do |node| if(node['emailAddress'] =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i) - if !node['firstName'].to_s.empty? and !node['lastName'].to_s.empty? and node['active'].to_s == '1' - user = JiraUser.new(node) - - # Set user names (first name, last name) - #user.jira_firstName = node["firstName"] - #user.jira_lastName = node["lastName"] - - # Set email address - user.jira_emailAddress = node["lowerEmailAddress"] - - user.jira_name = node["lowerUserName"] - - users.push(user) - puts "Found JIRA user: #{user.jira_name}" + if !node['firstName'].to_s.empty? and !node['lastName'].to_s.empty? + user = JiraUser.new(node) + user.jira_emailAddress = node["lowerEmailAddress"] + user.jira_name = node["lowerUserName"] + user.jira_active = node['active'] + users.push(user) + puts "Found JIRA user: #{user.jira_name}" end end end @@ -767,13 +814,10 @@ end # the key will be the identifier projs = [] # $doc.elements.each('/*/Project') do |node| - puts "before" $doc.xpath('/*/Project').each do |node| proj = JiraProject.new(node) projs.push(proj) end - puts "after" - puts projs.length() migrated_projects = {} projs.each do |p| @@ -933,8 +977,6 @@ namespace :jira_migration do if t.nil? Tracker.new(name: value) end - puts "key: " + key - puts "value: " + value t.save! t.reload $MIGRATED_ISSUE_TYPES[key] = t @@ -951,9 +993,7 @@ namespace :jira_migration do if s.nil? s = IssueStatus.new(name: value) end - puts "key: " + key - puts "value: " + value - s.save! + s.save! s.reload $MIGRATED_ISSUE_STATUS[key] = s end @@ -1035,9 +1075,7 @@ namespace :jira_migration do ##################################### Tests ########################################## desc "Just pretty print Jira Projects on screen" task :test_parse_projects => :environment do - puts "before parse projects" projects = JiraMigration.parse_projects() - puts "these are the projects: " projects.each {|p| pp(p.run_all_redmine_fields) } end @@ -1088,7 +1126,8 @@ namespace :jira_migration do :migrate_groups, :migrate_issues, :migrate_comments, - :migrate_attachments] do + :migrate_attachments + ] do puts "All migrations done! :-)" end