-
Notifications
You must be signed in to change notification settings - Fork 0
/
jira.go
114 lines (103 loc) · 3.63 KB
/
jira.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package githubjirabridge
import (
"context"
"crypto/subtle"
"encoding/json"
"net/http"
"regexp"
"strconv"
"github.com/andygrunwald/go-jira"
"github.com/google/go-github/v42/github"
)
var (
issueStubRegexp = regexp.MustCompile(`(?m)^https://github.com/(?P<owner>[^/]+)/(?P<repo>[^/]+)/issues/(?P<number>\d+)\n\n` + jiraStubSignature + "$")
)
func handleJiraIssueWebhook(w http.ResponseWriter, req *http.Request) {
logger.Println("handling jira issue webhook")
token := req.URL.Query().Get("token")
if token == "" {
logger.Println("missing jira webhook token")
w.WriteHeader(http.StatusForbidden)
return
}
if subtle.ConstantTimeCompare([]byte(jiraWebhookToken), []byte(token)) == 0 {
logger.Println("invalid jira webhook token")
w.WriteHeader(http.StatusForbidden)
return
}
var issueEvent JiraIssueEvent
if err := json.NewDecoder(req.Body).Decode(&issueEvent); err != nil {
logger.Printf("error parsing jira webhook: %s\n", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
logger.Printf("received jira issue webhook for issue %s", issueEvent.Issue.ID)
for _, changelogItem := range issueEvent.Changelog.Items {
desiredStatusGithubLabel := jiraStatusToGithubLabels[changelogItem.ToString]
if changelogItem.Field == "status" && desiredStatusGithubLabel != "" {
logger.Printf("jira issue %s has changed status to %s, will ensure that related Github issue has label %s\n", issueEvent.Issue.Key, changelogItem.ToString, desiredStatusGithubLabel)
handleStatusChangedJiraIssue(req.Context(), w, issueEvent, desiredStatusGithubLabel)
return
}
}
logger.Printf("will do nothing with jira issue %s\n", issueEvent.Issue.Key)
}
func handleStatusChangedJiraIssue(ctx context.Context, w http.ResponseWriter, issueEvent JiraIssueEvent, desiredStatusGithubLabel string) {
owner, repo, number := parseGithubIssueFromJiraStub(issueEvent.Issue.Fields.Description)
if number == 0 {
logger.Printf("error parsing Github issue details from jira issue %s\n", issueEvent.Issue.Key)
w.WriteHeader(http.StatusInternalServerError)
return
}
ghIssue, _, err := githubClient.Issues.Get(ctx, owner, repo, number)
if err != nil {
logger.Printf("error getting issue #%d: %s\n", number, err)
w.WriteHeader(http.StatusInternalServerError)
return
}
labels := ghIssue.Labels
var newLabels []string
for _, label := range labels {
if !isGithubStatusLabel(*label.Name) && *label.Name != githubTriageLabel {
newLabels = append(newLabels, *label.Name)
}
}
newLabels = append(newLabels, desiredStatusGithubLabel)
issuePatch := &github.IssueRequest{Labels: &newLabels}
_, _, err = githubClient.Issues.Edit(ctx, owner, repo, number, issuePatch)
if err != nil {
logger.Printf("error updating issue #%d: %s\n", number, err)
w.WriteHeader(http.StatusInternalServerError)
return
}
}
type JiraIssueEvent struct {
Issue jira.Issue `json:"issue"`
Changelog jira.ChangelogHistory `json:"changelog"`
}
func parseGithubIssueFromJiraStub(description string) (string, string, int) {
submatches := issueStubRegexp.FindStringSubmatch(description)
var owner, repo, numberStr string
for i, name := range issueStubRegexp.SubexpNames() {
if name == "owner" && i < len(submatches) {
owner = submatches[i]
} else if name == "repo" && i < len(submatches) {
repo = submatches[i]
} else if name == "number" && i < len(submatches) {
numberStr = submatches[i]
}
}
number, err := strconv.Atoi(numberStr)
if err != nil {
return "", "", 0
}
return owner, repo, number
}
func isGithubStatusLabel(label string) bool {
for _, githubLabel := range jiraStatusToGithubLabels {
if label == githubLabel {
return true
}
}
return false
}