Skip to content

Commit

Permalink
Introduce a "TranslatedIssue" with body translated from GitHub Markdo…
Browse files Browse the repository at this point in the history
…wn to JIRA formatting. Cover most of the cases except tables.
  • Loading branch information
andrivet committed Nov 30, 2017
1 parent e1e8326 commit de3c446
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 11 deletions.
2 changes: 1 addition & 1 deletion lib/comments.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func CompareComments(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue,
return nil
}

ghComments, err := ghClient.ListComments(ghIssue)
ghComments, err := ghClient.ListComments(github.Issue(ghIssue))
if err != nil {
return err
}
Expand Down
110 changes: 100 additions & 10 deletions lib/issues.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/coreos/issue-sync/cfg"
"github.com/coreos/issue-sync/lib/clients"
"github.com/google/go-github/github"
"regexp"
)

// dateFormat is the format used for the Last IS Update field
Expand Down Expand Up @@ -46,18 +47,19 @@ func CompareIssues(config cfg.Config, ghClient clients.GitHubClient, jiraClient

for _, ghIssue := range ghIssues {
found := false
ghTranslatedIssue := NewTranslatedIssue(ghIssue)
for _, jIssue := range jiraIssues {
id, _ := jIssue.Fields.Unknowns.Int(config.GetFieldKey(cfg.GitHubID))
if int64(*ghIssue.ID) == id {
found = true
if err := UpdateIssue(config, ghIssue, jIssue, ghClient, jiraClient); err != nil {
if err := UpdateIssue(config, ghTranslatedIssue, jIssue, ghClient, jiraClient); err != nil {
log.Errorf("Error updating issue %s. Error: %v", jIssue.Key, err)
}
break
}
}
if !found {
if err := CreateIssue(config, ghIssue, ghClient, jiraClient); err != nil {
if err := CreateIssue(config, ghTranslatedIssue, ghClient, jiraClient); err != nil {
log.Errorf("Error creating issue for #%d. Error: %v", *ghIssue.Number, err)
}
}
Expand All @@ -68,15 +70,15 @@ func CompareIssues(config cfg.Config, ghClient clients.GitHubClient, jiraClient

// DidIssueChange tests each of the relevant fields on the provided JIRA and GitHub issue
// and returns whether or not they differ.
func DidIssueChange(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue) bool {
func DidIssueChange(config cfg.Config, ghIssue TranslatedIssue, jIssue jira.Issue) bool {
log := config.GetLogger()

log.Debugf("Comparing GitHub issue #%d and JIRA issue %s", ghIssue.GetNumber(), jIssue.Key)

anyDifferent := false

anyDifferent = anyDifferent || (ghIssue.GetTitle() != jIssue.Fields.Summary)
anyDifferent = anyDifferent || (ghIssue.GetBody() != jIssue.Fields.Description)
anyDifferent = anyDifferent || (ghIssue.GetTranslatedBody() != jIssue.Fields.Description)

key := config.GetFieldKey(cfg.GitHubStatus)
field, err := jIssue.Fields.Unknowns.String(key)
Expand Down Expand Up @@ -109,7 +111,7 @@ func DidIssueChange(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue)
// UpdateIssue compares each field of a GitHub issue to a JIRA issue; if any of them
// differ, the differing fields of the JIRA issue are updated to match the GitHub
// issue.
func UpdateIssue(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue, ghClient clients.GitHubClient, jClient clients.JIRAClient) error {
func UpdateIssue(config cfg.Config, ghIssue TranslatedIssue, jIssue jira.Issue, ghClient clients.GitHubClient, jClient clients.JIRAClient) error {
log := config.GetLogger()

log.Debugf("Updating JIRA %s with GitHub #%d", jIssue.Key, *ghIssue.Number)
Expand All @@ -121,7 +123,7 @@ func UpdateIssue(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue, ghC
fields.Unknowns = map[string]interface{}{}

fields.Summary = ghIssue.GetTitle()
fields.Description = ghIssue.GetBody()
fields.Description = ghIssue.GetTranslatedBody()
fields.Unknowns[config.GetFieldKey(cfg.GitHubStatus)] = ghIssue.GetState()
fields.Unknowns[config.GetFieldKey(cfg.GitHubReporter)] = ghIssue.User.GetLogin()

Expand Down Expand Up @@ -160,7 +162,7 @@ func UpdateIssue(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue, ghC
return err
}

if err := CompareComments(config, ghIssue, issue, ghClient, jClient); err != nil {
if err := CompareComments(config, ghIssue.Issue, issue, ghClient, jClient); err != nil {
return err
}

Expand All @@ -169,7 +171,7 @@ func UpdateIssue(config cfg.Config, ghIssue github.Issue, jIssue jira.Issue, ghC

// CreateIssue generates a JIRA issue from the various fields on the given GitHub issue, then
// sends it to the JIRA API.
func CreateIssue(config cfg.Config, issue github.Issue, ghClient clients.GitHubClient, jClient clients.JIRAClient) error {
func CreateIssue(config cfg.Config, issue TranslatedIssue, ghClient clients.GitHubClient, jClient clients.JIRAClient) error {
log := config.GetLogger()

log.Debugf("Creating JIRA issue based on GitHub issue #%d", *issue.Number)
Expand All @@ -180,7 +182,7 @@ func CreateIssue(config cfg.Config, issue github.Issue, ghClient clients.GitHubC
},
Project: config.GetProject(),
Summary: issue.GetTitle(),
Description: issue.GetBody(),
Description: issue.GetTranslatedBody(),
Unknowns: map[string]interface{}{},
}

Expand Down Expand Up @@ -215,9 +217,97 @@ func CreateIssue(config cfg.Config, issue github.Issue, ghClient clients.GitHubC

log.Debugf("Created JIRA issue %s!", jIssue.Key)

if err := CompareComments(config, issue, jIssue, ghClient, jClient); err != nil {
if err := CompareComments(config, issue.Issue, jIssue, ghClient, jClient); err != nil {
return err
}

return nil
}

type TranslatedIssue struct {

github.Issue
TranslatedBody *string
}

func NewTranslatedIssue(issue github.Issue) TranslatedIssue {
body := GitHubToJiraBody(*(issue.Body))
return TranslatedIssue{issue, &body}
}

func (i *TranslatedIssue) GetTranslatedBody() string {
if i == nil || i.TranslatedBody == nil {
return ""
}
return *i.TranslatedBody
}

// Headings
var regexH6 = regexp.MustCompile(`(?m)^###### (.*)$`)
var regexH5 = regexp.MustCompile(`(?m)^##### (.*)$`)
var regexH4 = regexp.MustCompile(`(?m)^#### (.*)$`)
var regexH3 = regexp.MustCompile(`(?m)^### (.*)$`)
var regexH2 = regexp.MustCompile(`(?m)^## (.*)$`)
var regexH1 = regexp.MustCompile(`(?m)^# (.*)$`)

// Text Effects
var regexStrong1 = regexp.MustCompile(`(?U)\*\*([\s\S]*)\*\*`) // **strong**
var regexStrong2 = regexp.MustCompile(`(?U)__([\s\S]*)__`) // __strong__
var regexEmphasis1 = regexp.MustCompile(`(?U)\*([\s\S]*)\*`) // *emphasis*
var regexEmphasis2 = regexp.MustCompile(`(?U)_([\s\S]*)_`) // _emphasis_
var regexCitation = regexp.MustCompile(`(?U)<cite>([\s\S]*)<cite>`) // <cite>citation<cite>
var regexDeleted = regexp.MustCompile(`(?U)~~([\s\S])~~`) // ~~deleted~~
var regexInserted = regexp.MustCompile(`(?U)<ins>([\s\S]*)<ins>`) // <ins>insertion<ins>
var regexSuperscript = regexp.MustCompile(`(?U)<sup>([\s\S]*)<sup>`) // <sup>superscript<sup>
var regexSubscript = regexp.MustCompile(`(?U)<sub>([\s\S]*)<sub>`) // <sub>subscript<sub>
var regexMonospaced = regexp.MustCompile("(?U)`([\\s\\S]*)`") // `monospaced`
var regexQuote = regexp.MustCompile(`(?m)^>\s+(.*)$`) // > quote

// Links
var regexImage = regexp.MustCompile(`(?U)!\[(.*)\]\((.*)\)`) // ![alt](url)
var regexURL = regexp.MustCompile(`(?U)<(.*)>`) // <url>
var regexAltURL = regexp.MustCompile(`(?U)\[(.*)\]\((.*)\)`) // [alt](url)

// Advanced Formatting
var regexCode = regexp.MustCompile("(?mU)^\\`\\`\\`(\\w+)$\\n([\\s\\S]*)\\n^\\`\\`\\`")
var regexNoFormat = regexp.MustCompile("(?mU)^\\`\\`\\`$\\n([\\s\\S]*)\\n^\\`\\`\\`")

// TODO: Tables

// JIRA and GitHub (Markdown) have different markups. Translate from GitHub (Markdown) to JIRA.
// See https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=all
func GitHubToJiraBody(body string) string {

// Headings
body = regexH6.ReplaceAllString(body, "h6. $1")
body = regexH5.ReplaceAllString(body, "h5. $1")
body = regexH4.ReplaceAllString(body, "h3. $1")
body = regexH3.ReplaceAllString(body, "h3. $1")
body = regexH2.ReplaceAllString(body, "h2. $1")
body = regexH1.ReplaceAllString(body, "h1. $1")

// Text Effects
body = regexStrong1.ReplaceAllString(body, "*$1*")
body = regexStrong2.ReplaceAllString(body, "*$1*")
body = regexEmphasis1.ReplaceAllString(body, "_$1_")
body = regexEmphasis2.ReplaceAllString(body, "_$1_")
body = regexCitation.ReplaceAllString(body, "??$1??")
body = regexDeleted.ReplaceAllString(body, "-$1-")
body = regexInserted.ReplaceAllString(body, "+$1+")
body = regexSuperscript.ReplaceAllString(body, "^$1^")
body = regexSubscript.ReplaceAllString(body, "~$1~")
body = regexMonospaced.ReplaceAllString(body, "{{$1}}")
body = regexQuote.ReplaceAllString(body, "bq. $1")

// Links
body = regexImage.ReplaceAllString(body, "!$2|width=600!")
body = regexURL.ReplaceAllString(body, "[$1]")
body = regexAltURL.ReplaceAllString(body, "[$1|$2]")

// Advanced Formatting
body = regexCode.ReplaceAllString(body, `{code:$1}\n$2\n{code}`)
body = regexNoFormat.ReplaceAllString(body, `{noformat}\n$2\n{noformat}`)

return body
}

0 comments on commit de3c446

Please sign in to comment.