Skip to content

Commit

Permalink
Merge branch 'release/0.1.0b2'
Browse files Browse the repository at this point in the history
  • Loading branch information
shomatan committed Dec 12, 2017
2 parents d943eda + 4a96234 commit 40f409e
Show file tree
Hide file tree
Showing 12 changed files with 179 additions and 30 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ Migrate your projects from JIRA to [Backlog].
* Backlog
* [https://backlog.com](https://backlog.com/)


## DEMO

![Demo](https://www.backlog.jp/backlog-migration/backlog-jira-migration.gif)


## Requirements
* **Java 8**
* The Backlog Space's **administrator** roles.
Expand All @@ -19,7 +25,7 @@ Download

Please download the jar file from this link, and run from the command line as follows.

https://github.com/nulab/BacklogMigration-Jira/releases/download/0.1.0b1/backlog-migration-jira-0.1.0b1.jar
https://github.com/nulab/BacklogMigration-Jira/releases/download/0.1.0b2/backlog-migration-jira-0.1.0b2.jar

java -jar backlog-migration-jira-[latest version].jar

Expand Down Expand Up @@ -227,7 +233,7 @@ https://github.com/nulab/BacklogMigration-Jira/releases

こちらのリンクからjarファイルをダウンロードし、以下のようにコマンドラインから実行します。

https://github.com/nulab/BacklogMigration-Jira/releases/download/0.1.0b1/backlog-migration-jira-0.1.0b1.jar
https://github.com/nulab/BacklogMigration-Jira/releases/download/0.1.0b2/backlog-migration-jira-0.1.0b2.jar

java -jar backlog-migration-jira-[最新バージョン].jar

Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sbt.Keys._

lazy val projectVersion = "0.1.0b1"
lazy val projectVersion = "0.1.0b2"

lazy val commonSettings = Seq(
organization := "com.nulabinc",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.nulabinc.backlog.j2b.exporter

import com.nulabinc.backlog.migration.common.domain.BacklogIssue
import com.nulabinc.jira.client.domain.changeLog._

object ChangeLogIssueLinkConverter {

def convert(changeLogs: Seq[ChangeLog], backlogIssue: BacklogIssue): Seq[ChangeLog] = {
val displayStrings = changeLogs.flatMap { changeLog =>
changeLog.items
.filter { item => item.field == DefaultField("link_issue") }
.flatMap { _.toDisplayString }
}

val linkedIssueChangeLogItems = if (displayStrings.nonEmpty) {
val lastDescription = changeLogs.reverse.flatMap { changeLog =>
changeLog.items.reverse.find(_.field == DescriptionChangeLogItemField)
}.headOption.flatMap(_.toDisplayString)

Seq(
ChangeLog(
id = 0, // TODO: Check
author = changeLogs.last.author,
createdAt = changeLogs.last.createdAt,
items = Seq(
ChangeLogItem(
field = DescriptionChangeLogItemField,
fieldType = ChangeLogItem.FieldType.JIRA,
fieldId = Some(DescriptionFieldId),
from = None,
fromDisplayString = lastDescription,
to = None,
toDisplayString = Some(lastDescription.getOrElse("") + backlogIssue.description + "\n\n" + displayStrings.mkString("\n"))
)
)
)
)
} else {
Seq.empty[ChangeLog]
}

changeLogs ++ linkedIssueChangeLogItems
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.nulabinc.backlog.j2b.exporter

import javax.inject.Inject

import com.nulabinc.backlog.j2b.exporter.console.RemainingTimeCalculator
import com.nulabinc.backlog.j2b.jira.conf.JiraBacklogPaths
import com.nulabinc.backlog.j2b.jira.domain.mapping.MappingCollectDatabase
import com.nulabinc.backlog.j2b.jira.domain.{CollectData, JiraProjectKey}
Expand Down Expand Up @@ -65,7 +66,8 @@ class Exporter @Inject()(projectKey: JiraProjectKey,
val statuses = statusService.all()
val total = issueService.count()
val fields = fieldService.all()
fetchIssue(statuses, categories, versions, fields, 1, total, 0, 100)
val calculator = new RemainingTimeCalculator(total)
fetchIssue(calculator, statuses, categories, versions, fields, 1, total, 0, 100)

// version & milestone
versionsWriter.write(versions, mappingCollectDatabase.milestones)
Expand All @@ -86,7 +88,8 @@ class Exporter @Inject()(projectKey: JiraProjectKey,
collectedData
}

private def fetchIssue(statuses: Seq[Status],
private def fetchIssue(calculator: RemainingTimeCalculator,
statuses: Seq[Status],
components: Seq[Component],
versions: Seq[Version],
fields: Seq[Field],
Expand Down Expand Up @@ -155,10 +158,14 @@ class Exporter @Inject()(projectKey: JiraProjectKey,
// export issue comments
val categoryPlayedChangeLogs = ChangeLogsPlayer.play(ComponentChangeLogItemField, initializedBacklogIssue.categoryNames, issueWithFilteredChangeLogs.changeLogs)
val versionPlayedChangeLogs = ChangeLogsPlayer.play(FixVersion, initializedBacklogIssue.versionNames, categoryPlayedChangeLogs)
val changeLogs = ChangeLogStatusConverter.convert(versionPlayedChangeLogs, statuses)
val statusPlayedChangeLogs = ChangeLogStatusConverter.convert(versionPlayedChangeLogs, statuses)
val changeLogs = ChangeLogIssueLinkConverter.convert(statusPlayedChangeLogs, initializedBacklogIssue)

commentWriter.write(initializedBacklogIssue, comments, changeLogs, issue.attachments)

console(i + index.toInt, total.toInt)
// console(i + index.toInt, total.toInt)

calculator.progress(i + index.toInt, total.toInt, issue.summary)

val changeLogUsers = changeLogs.map(u => Some(u.author.name))
val changeLogItemUsers = changeLogs.flatMap { changeLog =>
Expand Down Expand Up @@ -194,7 +201,7 @@ class Exporter @Inject()(projectKey: JiraProjectKey,
}
}
}
fetchIssue(statuses, components, versions, fields, index + issues.length , total, startAt + maxResults, maxResults)
fetchIssue(calculator, statuses, components, versions, fields, index + issues.length , total, startAt + maxResults, maxResults)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.nulabinc.backlog.j2b.exporter.console

import java.util.Date

import com.nulabinc.backlog.migration.common.utils.{ConsoleOut, DateUtil, Logging, ProgressBar}
import com.osinka.i18n.Messages
import org.fusesource.jansi.Ansi
import org.fusesource.jansi.Ansi.ansi

class RemainingTimeCalculator(totalSize: Long) extends Logging {

private var failed = 0
private var date = ""

private case class RemainingTime(totalSize: Long, lastTime: Long = System.currentTimeMillis(), totalElapsedTime: Long = 0, count: Long = 0) {

def action(): RemainingTime = {
val elapsedTime: Long = System.currentTimeMillis() - this.lastTime
this.copy(
lastTime = System.currentTimeMillis(),
totalElapsedTime = totalElapsedTime + elapsedTime,
count = count + 1
)
}

def average: Float = totalElapsedTime.toFloat / count.toFloat

def remaining: Long = totalSize - count

def remainingTime: Long = (remaining * average).toLong
}

private var remainingTime = RemainingTime(totalSize)

private[this] var newLine = false
private[this] var isMessageMode = false

def progress(indexOfDate: Int, totalOfDate: Int, summary: String): Unit = {
newLine = indexOfDate == 1
clear()
remainingTime = remainingTime.action()
val message = remaining()
ConsoleOut.outStream.println(message)
isMessageMode = false
}

private def clear(): Unit = {
if (newLine && !isMessageMode) {
ConsoleOut.outStream.println()
}
(0 until 3).foreach { _ =>
ConsoleOut.outStream.print(ansi.cursorLeft(999).cursorUp(1).eraseLine(Ansi.Erase.ALL))
}
ConsoleOut.outStream.flush()
newLine = false
}

private def remaining(): String = {
val progressBar = ProgressBar.progressBar(remainingTime.count.toInt, remainingTime.totalSize.toInt)
val time = Messages("export.remaining_time", DateUtil.timeFormat(new Date(remainingTime.remainingTime)))
s"$progressBar $time"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ case object TimeSpentChangeLogItemField extends ChangeLogItemField("timespent")
case object WorkIdChangeLogItemField extends ChangeLogItemField("WorklogId")
case object TimeEstimateChangeLogItemField extends ChangeLogItemField("timeestimate")
case object SprintChangeLogItemField extends ChangeLogItemField("Sprint")
case object DescriptionChangeLogItemField extends ChangeLogItemField("description")
case class DefaultField(name: String) extends ChangeLogItemField(name)

object ChangeLogItemField {
Expand All @@ -53,6 +54,7 @@ object ChangeLogItemField {
case WorkIdChangeLogItemField.value => WorkIdChangeLogItemField
case TimeEstimateChangeLogItemField.value => TimeEstimateChangeLogItemField
case SprintChangeLogItemField.value => SprintChangeLogItemField
case DescriptionChangeLogItemField.value => DescriptionChangeLogItemField
case v => DefaultField(v)
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/application.conf
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
application {
name = "Backlog Migration for Jira"
version = "0.1.0b1"
version = "0.1.0b2"
title = ${application.name} ${application.version} (c) nulab.inc
export-limit-at-once = 100
mixpanel.token = "5be8b628b7103858164142d02cb38347"
mixpanel.backlogtool.token = "0512c52e553b9283143bed99e61c27e4"
mixpanel.product = "jira"
language=default
akka.mailbox-pool = 100
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/messages.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ cli.help.projectKey=Your JIRA project identifier.(required) Example:--projectKey
cli.confirm_recreate=The mapping file {0} already exists. Do you want to overwrite it? (y/n [n]):
cli.backlog_project_already_exist=Project "{0}" already exists. Do you want to import issues and wikis to project "{0}"? (Check the README file for details.) (y/n [n]):
cli.error.unknown=Unknown error
cli.error.args=Invalid arguments. Please check the following.
cli.cancel=Import has been canceled.
cli.invalid_setup=Setup is incomplete. Please complete the set up with the sub-command "export". Add "--help" option to know about the "export" command.
cli.warn.not.latest=The latest version [{0}] has been released. The current version is [{1}].
Expand Down Expand Up @@ -168,6 +169,8 @@ import.error.failed.comment=Could not register comment on issue [{0}]. : {1}
export.start=Start export.
export.finish=Export completed. Next step \n\n1. Edit mapping files.\n2. Execute the following command to import (replace with your JIRA password).\n\n--------------------------------------\n\n{0}\n\n--------------------------------------
export.attachment.empty=Attachment: {0} -> {1}
export.remaining_time=[Remaining time:{0}]
export.date.execute={2} {1} about {0}.

# -----------------------------------------------------------------------------
# Convert
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/messages_ja.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ cli.help.projectKey=移行したいJIRAプロジェクトのプロジェクト
cli.confirm_recreate={0}マッピングファイルが既にあります。上書きしますか? (y/n [n]):
cli.backlog_project_already_exist=プロジェクト[{0}]はBacklog内に既に存在します。\nプロジェクト[{0}]に課題とWikiをインポートしますか?(追加インポートの仕様については、詳細READMEを参照ください) (y/n [n]):
cli.error.unknown=予期しないエラーが発生しました。
cli.error.args=不正な引数です。以下をご確認下さい。
cli.cancel=インポートを中止しました。
cli.invalid_setup=エクスポートが不十分です。サブコマンド[export]を使用し再度エクスポートしてください。[export]コマンドについて知るには、[--help]オプションをつけて実行してください。
cli.warn.not.latest=最新バージョン[{0}]がリリースされています。現在のバージョンは[{1}]です。
Expand Down Expand Up @@ -169,6 +170,8 @@ import.error.failed.comment=コメントを課題[{0}]に登録できません
export.start=エクスポートを開始します。
export.finish=エクスポートが完了しました。 次のステップは \n\n1. マッピングファイルを編集します。\n2. インポートするために以下のコメントを実行します。 (JIRAのパスワードは修正してください)\n\n--------------------------------------\n\n{0}\n\n--------------------------------------
export.attachment.empty=添付ファイル: {0} -> {1}
export.remaining_time=[残り時間:{0}]
export.date.execute={0}の{1}を{2}

# -----------------------------------------------------------------------------
# Convert
Expand Down
23 changes: 16 additions & 7 deletions src/main/scala/com/nulabinc/backlog/j2b/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import com.osinka.i18n.Messages
import org.fusesource.jansi.AnsiConsole
import org.rogach.scallop.{ScallopConf, Subcommand}

import scala.util.{Failure, Success, Try}

class CommandLineInterface(arguments: Seq[String]) extends ScallopConf(arguments) with BacklogConfiguration with Logging {

banner(
Expand Down Expand Up @@ -105,15 +107,22 @@ object J2B extends BacklogConfiguration with Logging {
// Run
try {
val cli = new CommandLineInterface(args)
val config = getConfiguration(cli)
val nextCommand = new NextCommand(args)

cli.subcommand match {
case Some(cli.importCommand) => J2BCli.`import`(config)
case Some(cli.exportCommand) => J2BCli.export(config, nextCommand)
case _ => J2BCli.help()
getConfiguration(cli) match {
case Success(config) =>
cli.subcommand match {
case Some(cli.importCommand) => J2BCli.`import`(config)
case Some(cli.exportCommand) => J2BCli.export(config, nextCommand)
case _ => J2BCli.help()
}
exit(0)
case Failure(failure) =>
ConsoleOut.error(s"${Messages("cli.error.args")}")
logger.error(failure.getMessage)
J2BCli.help()
exit(1)
}
exit(0)
} catch {
case e: Throwable =>
logger.error(e.getMessage, e)
Expand All @@ -122,7 +131,7 @@ object J2B extends BacklogConfiguration with Logging {
}
}

private[this] def getConfiguration(cli: CommandLineInterface) = {
private[this] def getConfiguration(cli: CommandLineInterface) = Try {
val keys: Array[String] = cli.importCommand.projectKey().split(":")
val jira: String = keys(0)
val backlog: String = if (keys.length == 2) keys(1) else keys(0).toUpperCase.replaceAll("-", "_")
Expand Down
24 changes: 15 additions & 9 deletions src/main/scala/com/nulabinc/backlog/j2b/cli/Tracker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,23 @@ trait Tracker extends BacklogConfiguration {
def tracking(config: AppConfiguration, spaceService: SpaceService, userService: UserService) = {
Try {
val environment = spaceService.environment()
val data = TrackingData(product = mixpanelProduct,
envname = environment.name,
spaceId = environment.spaceId,
userId = userService.myself().id,
srcUrl = config.jiraConfig.url,
dstUrl = config.backlogConfig.url,
srcProjectKey = config.jiraConfig.projectKey,
dstProjectKey = config.backlogConfig.projectKey,
val data = TrackingData(
product = mixpanelProduct,
envname = environment.name,
spaceId = environment.spaceId,
userId = userService.myself().id,
srcUrl = config.jiraConfig.url,
dstUrl = config.backlogConfig.url,
srcProjectKey = config.jiraConfig.projectKey,
dstProjectKey = config.backlogConfig.projectKey,
srcSpaceCreated = "",
dstSpaceCreated = spaceService.space().created)
MixpanelUtil.track(token = mixpanelToken, data = data)
val backlogToolEnvNames = Seq(
"backlogtool",
"us-6"
)
val token = if (backlogToolEnvNames.contains(environment.name)) mixpanelBacklogtoolToken else mixpanelToken
MixpanelUtil.track(token = token, data = data)
}
}
}
15 changes: 10 additions & 5 deletions src/test/scala/com/nulabinc/backlog/j2b/CompareSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.nulabinc.backlog.migration.common.convert.writes.UserWrites
import com.nulabinc.backlog4j.{CustomFieldSetting, IssueComment, ResponseList}
import com.nulabinc.backlog4j.api.option.{GetIssuesParams, QueryParams}
import com.nulabinc.backlog4j.internal.json.customFields._
import com.nulabinc.jira.client.domain.changeLog.LinkChangeLogItemField
import com.nulabinc.jira.client.domain.field._
import com.nulabinc.jira.client.domain.issue._
import org.scalatest.{DiagrammedAssertions, FlatSpec, Matchers}
Expand Down Expand Up @@ -132,9 +133,6 @@ class CompareSpec extends FlatSpec

maybeBacklogIssue.map { backlogIssue =>

// description
jiraIssue.description.getOrElse("") should equal(backlogIssue.getDescription)

// issue type
jiraIssue.issueType.name should equal(backlogIssue.getIssueType.getName)

Expand Down Expand Up @@ -270,7 +268,7 @@ class CompareSpec extends FlatSpec
jiraCommentService.issueComments(jiraIssue).filterNot { jiraComment =>
attachmentCommentPattern.findFirstIn(jiraComment.body).isDefined
}.map { jiraComment =>
val backlogComment = backlogAllComments.find(_.getContent == jiraComment.body)
val backlogComment = backlogAllComments.find(_.getContent == jiraComment.body.trim)
backlogComment should not be empty
assertUser(jiraComment.author, backlogComment.get.getCreatedUser)
timestampToString(jiraComment.createdAt.toDate) should be(timestampToString(backlogComment.get.getCreated))
Expand All @@ -280,14 +278,21 @@ class CompareSpec extends FlatSpec
// Test
// - creator is same
// - created at is same
jiraIssueService.changeLogs(jiraIssue).map { jiraChangeLog =>
val jiraChangeLogs = jiraIssueService.changeLogs(jiraIssue)
jiraChangeLogs.map { jiraChangeLog =>
val backlogChangelog = backlogAllComments.find { backlogComment =>
timestampToString(backlogComment.getCreated) == timestampToString(jiraChangeLog.createdAt.toDate)
}
backlogChangelog should not be empty
assertUser(jiraChangeLog.author, backlogChangelog.get.getCreatedUser)
}

// description
val links = jiraChangeLogs.flatMap(_.items.filter(_.field == LinkChangeLogItemField).flatMap(_.toDisplayString))
val linksReplace = "\n\n" + links.mkString("\n")
val backlogDescription = backlogIssue.getDescription.replace(linksReplace, "")
jiraIssue.description.getOrElse("") should equal(backlogDescription)

}

}
Expand Down

0 comments on commit 40f409e

Please sign in to comment.