@@ -8,17 +8,14 @@ import (
8
8
"log"
9
9
"os"
10
10
"regexp"
11
+ "strings"
11
12
12
13
"gopkg.in/andygrunwald/go-jira.v1"
13
14
)
14
15
15
16
const (
16
- // Version of jitic
17
- Version = "0.2.2"
18
- )
19
-
20
- var (
21
- logger * log.Logger
17
+ // Version reflects the version of jitic
18
+ Version = "1.0.0"
22
19
)
23
20
24
21
func main () {
@@ -33,90 +30,51 @@ func main() {
33
30
)
34
31
flag .Parse ()
35
32
36
- // Set logger (throw messages away)
37
- logger = log .New (ioutil .Discard , "" , log .LstdFlags )
33
+ // Set logger (throw messages away).
34
+ // In a verbose mode we output the messages to stdout
35
+ logger := log .New (ioutil .Discard , "" , log .LstdFlags )
38
36
if * flagVerbose {
39
37
logger = log .New (os .Stdout , "" , log .LstdFlags )
40
38
}
41
39
42
- // Output the version and exit
40
+ // Output the version, exit
43
41
if * flagVersion {
44
42
fmt .Printf ("jitic v%s\n " , Version )
45
43
os .Exit (0 )
46
44
}
47
45
48
- // If we don`t get a JIRA instance, exit
46
+ // If we don`t have a JIRA instance URL , exit
49
47
if len (* jiraURL ) <= 0 {
50
48
logger .Fatal ("No JIRA Instance provided. Please set the URL of the JIRA instance by -url parameter." )
51
49
}
52
50
53
- // Collect all issue keys
54
- var issues []string
55
- if len (* issueMessage ) > 0 {
56
- issues = GetIssuesOutOfMessage (* issueMessage )
57
- }
51
+ issueText := getTextToAnalyze (* issueMessage , * inputStdin )
52
+ issues := getIssuesOutOfMessage (issueText )
58
53
59
- // If we don`t get any issue, we will just exit here.
60
- if * inputStdin == false && len (issues ) == 0 {
61
- logger .Fatal ("No JIRA-Issue(s) found." )
54
+ // If we don`t get any issues, exit
55
+ if len (issues ) == 0 {
56
+ logger .Fatalf ("No JIRA-Issue(s) found in text '%s'." , issueText )
62
57
}
63
58
64
- // Get the JIRA client
65
- jiraInstance , err := jira .NewClient (nil , * jiraURL )
59
+ jiraClient , err := getJIRAClient (* jiraURL , * jiraUsername , * jiraPassword )
66
60
if err != nil {
67
- logger .Fatalf ( "JIRA client can`t be initialized: %s" , err )
61
+ logger .Fatal ( err )
68
62
}
69
63
70
- // Only provide authentification if a username and password was applied
71
- if len ( * jiraUsername ) > 0 && len ( * jiraPassword ) > 0 {
72
- ok , err := jiraInstance . Authentication . AcquireSessionCookie ( * jiraUsername , * jiraPassword )
73
- if ok == false || err != nil {
74
- logger .Fatalf ( "jitic can`t authentificate user %s against the JIRA instance %s: %s" , * jiraUsername , * jiraURL , err )
64
+ // Loop over all issues and check if they are correct / valid
65
+ for _ , issueFromUser := range issues {
66
+ err := checkIfIssue ( issueFromUser , jiraClient )
67
+ if err != nil {
68
+ logger .Fatal ( err )
75
69
}
76
70
}
77
71
78
- // If the issues will be applied by argument
79
- if * inputStdin == false {
80
- IssueLoop (issues , jiraInstance )
81
- }
82
-
83
- // If the issues will be applied by stdin
84
- if * inputStdin {
85
- ReadIssuesFromStdin (jiraInstance )
86
- }
87
-
88
72
os .Exit (0 )
89
73
}
90
74
91
- // ReadIssuesFromStdin will read content vom standard input and search for JIRA issue keys
92
- // If an issue key was found a check with the incoming jiraInstance will be done.
93
- func ReadIssuesFromStdin (jiraInstance * jira.Client ) {
94
- scanner := bufio .NewScanner (os .Stdin )
95
- for scanner .Scan () {
96
- issues := GetIssuesOutOfMessage (scanner .Text ())
97
- // If no issue can be found
98
- if len (issues ) == 0 {
99
- logger .Fatal ("No JIRA-Issue(s) found." )
100
- }
101
- IssueLoop (issues , jiraInstance )
102
- }
103
- }
104
-
105
- // IssueLoop will loop over issues and request jiraInstance to check if the issue exists.
106
- func IssueLoop (issues []string , jiraInstance * jira.Client ) {
107
- for _ , incomingIssue := range issues {
108
- issue , response , err := jiraInstance .Issue .Get (incomingIssue , nil )
109
- if c := response .StatusCode ; err != nil || (c < 200 && c > 299 ) {
110
- logger .Fatalf ("Issue %s: %s" , incomingIssue , response .Status )
111
- }
112
- if incomingIssue != issue .Key {
113
- logger .Fatalf ("Issue %s is not the same as %s (provided by JIRA)" , incomingIssue , issue .Key )
114
- }
115
- }
116
- }
117
-
118
- // GetIssuesOutOfMessage will retrieve all JIRA issue keys out of a text.
75
+ // getIssuesOutOfMessage will retrieve all JIRA issue keys out of a text.
119
76
// A text can be everything, but a use case is e.g. a commit message.
77
+ //
120
78
// Example:
121
79
// Text: WEB-22861 remove authentication prod build for now
122
80
// Result: WEB-22861
@@ -126,14 +84,11 @@ func IssueLoop(issues []string, jiraInstance *jira.Client) {
126
84
//
127
85
// @link https://confluence.atlassian.com/display/STASHKB/Integrating+with+custom+JIRA+issue+key
128
86
// @link https://answers.atlassian.com/questions/325865/regex-pattern-to-match-jira-issue-key
129
- func GetIssuesOutOfMessage (issueMessage string ) []string {
130
- // Normally i would use
131
- // ((?<!([A-Z]{1,10})-?)[A-Z]+-\d+)
132
- // See http://stackoverflow.com/questions/26771592/negative-look-ahead-go-regular-expressions
87
+ func getIssuesOutOfMessage (message string ) []string {
133
88
var issues []string
134
89
re := regexp .MustCompile ("(?i)([A-Z]+)-(\\ d+)" )
135
90
136
- parts := re .FindAllStringSubmatch (issueMessage , - 1 )
91
+ parts := re .FindAllStringSubmatch (message , - 1 )
137
92
for _ , v := range parts {
138
93
// If the issue number > 0 (to avoid matches for PSR-0)
139
94
if v [2 ] > "0" {
@@ -143,3 +98,68 @@ func GetIssuesOutOfMessage(issueMessage string) []string {
143
98
144
99
return issues
145
100
}
101
+
102
+ // checkIfIssue checks if issue exists in the JIRA instance.
103
+ // If not an error will be returned.
104
+ func checkIfIssue (issue string , jiraClient * jira.Client ) error {
105
+ JIRAIssue , resp , err := jiraClient .Issue .Get (issue , nil )
106
+ if c := resp .StatusCode ; err != nil || (c < 200 || c > 299 ) {
107
+ return fmt .Errorf ("JIRA Request for issue %s returned %s (%d)" , issue , resp .Status , resp .StatusCode )
108
+ }
109
+
110
+ // Make issues uppercase to be able to compare Web-1234 with WEB-1234
111
+ upperIssue := strings .ToUpper (issue )
112
+ upperJIRAIssue := strings .ToUpper (JIRAIssue .Key )
113
+ if upperIssue != upperJIRAIssue {
114
+ return fmt .Errorf ("Issue %s is not the same as %s (provided by JIRA)" , upperIssue , upperJIRAIssue )
115
+ }
116
+
117
+ return nil
118
+ }
119
+
120
+ // getJIRAClient will return a valid JIRA api client.
121
+ func getJIRAClient (instanceURL , username , password string ) (* jira.Client , error ) {
122
+ client , err := jira .NewClient (nil , instanceURL )
123
+ if err != nil {
124
+ return nil , fmt .Errorf ("JIRA client can`t be initialized: %s" , err )
125
+ }
126
+
127
+ // Only provide authentification if a username and password was applied
128
+ if len (username ) > 0 && len (password ) > 0 {
129
+ ok , err := client .Authentication .AcquireSessionCookie (username , password )
130
+ if ok == false || err != nil {
131
+ return nil , fmt .Errorf ("jitic can`t authentificate user %s against the JIRA instance %s: %s" , username , instanceURL , err )
132
+ }
133
+ }
134
+
135
+ return client , nil
136
+ }
137
+
138
+ // readStdin will read content from stdin and return the content.
139
+ func readStdin () string {
140
+ var text string
141
+ scanner := bufio .NewScanner (os .Stdin )
142
+ for scanner .Scan () {
143
+ text += scanner .Text ()
144
+ }
145
+
146
+ return text
147
+ }
148
+
149
+ // getTextToAnalyze will return the text jitic should analyze.
150
+ // From command line argument or/and stdin
151
+ func getTextToAnalyze (argText string , inputStdin bool ) string {
152
+ var text string
153
+
154
+ // We get a message via cmd argument
155
+ if len (argText ) > 0 {
156
+ text = argText
157
+ }
158
+
159
+ // If stdin is activated
160
+ if inputStdin {
161
+ text += readStdin ()
162
+ }
163
+
164
+ return text
165
+ }
0 commit comments