WTBuildHelpers 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f40d9ad925699b7a02d7a6817d1c2a07b637216d
4
- data.tar.gz: 16a61b7557ad2279dc49f26d6d5c83797f3216e0
3
+ metadata.gz: e6418a62d2f8b5af8789b0e040b099192d655c44
4
+ data.tar.gz: 518f10e071661fb471d5bf1350ef0d7f29dce4e9
5
5
  SHA512:
6
- metadata.gz: dd0434dc23d7b2449b8340f065c8e9d5bc2aed772511c31e5e6c5e387df8741bfbcf27c3dbf73186b3f370530c74eed68917c2007400ced31914751e6a947d6a
7
- data.tar.gz: eaa64b3dd5bf6d43aedfeb38cdd30dc2a1b7a2d0e7902e1a3d211ec1d0d7ec097aa8ad0e75b5f57bb94ab169f28eea1291cfb7fc0a79fb4c47ab20866c71812f
6
+ metadata.gz: b751fd611f57471cd40ddadbbd4f74fdca88f14539bc8bc3788adf9391f5c8356b11b2d922ee86dfbaa21aa5b5280eaf4427785c8674b63e043e3bba29fa1f3b
7
+ data.tar.gz: acf144d2c25e5e1e666da093fcdc9affb720574444f3f4c86e95b8cc452ea0fc2feacc3ae910b69d572f162c2b54761d5d0c987cb5e54e98106e1e8902bb579c
data/README.md CHANGED
@@ -3,9 +3,12 @@
3
3
  This is a set of scripts for multiple platforms to help with building and testing tasks
4
4
  specific to WillowTree's pipelines and integrations.
5
5
 
6
- ### JIRA
6
+ All commands are run with the wtbuild command. If you need help on a specific command, run wtbuild help [command]
7
7
 
8
- #### perform_jira_update
8
+ JIRA
9
+ ====
10
+
11
+ #### jira_update_from_build
9
12
  Updates any jira tickets based on the supplied git commit range.
10
13
 
11
14
  Environment Variables
@@ -17,22 +20,26 @@ Environment Variables
17
20
  - TRAVIS_BUILD_NUMBER - build number
18
21
  - Supplied by TeamCity
19
22
  - BUILD_NUMBER - build number
20
- - TRAMCITY_COMMIT_RANGE - created by teamcity_git_range_fetch
23
+ - TRAMCITY_COMMIT_RANGE - created by teamcity_fetch_git_range
21
24
 
22
25
  Command Line Usage:
23
26
  ```bash
24
- $ wtbuild_jira_update
27
+ $ wtbuild jira_update_from_build
25
28
  ```
26
29
 
27
30
  ### TeamCity
28
- #### teamcity_git_range_fetch
31
+ #### teamcity_fetch_git_range
29
32
  Fetches the last good build from the TeamCity server and uses that to produce the a git commit range.
30
33
 
31
- Command Line Usage (from TeamCity build step):
34
+ Command Line Usage (when run as its own TeamCity build step):
32
35
  ```bash
33
- $ wtbuild_teamcity_fetch_git_range "%teamcity.serverUrl%" "%system.teamcity.buildType.id%" "%system.teamcity.auth.userId%" "%system.teamcity.auth.password%"``
36
+ $ wtbuild teamcity_fetch_git_range --url "%teamcity.serverUrl%" --build_type "%system.teamcity.buildType.id%" --username "%system.teamcity.auth.userId%" --password "%system.teamcity.auth.password%"`
34
37
  ```
35
38
 
39
+ Command Line Usage (when run as part of a larger build step):
40
+ ```bash
41
+ $ export TEAMCITY_COMMIT_RANGE=`$ wtbuild teamcity_fetch_git_range --url "%teamcity.serverUrl%" --build_type "%system.teamcity.buildType.id%" --username "%system.teamcity.auth.userId%" --password "%system.teamcity.auth.password%" --export`
42
+ ```
36
43
 
37
44
  ## Installation
38
45
 
data/exe/wtbuild ADDED
@@ -0,0 +1,57 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'WTBuildHelpers'
4
+
5
+ Command = Struct.new(:command, :description, :help_function, :run_function)
6
+
7
+ commands = [
8
+
9
+ # JIRA section
10
+ Command.new(
11
+ "jira_update_from_build",
12
+ "Update JIRA by transitioning any mentioned issues and settting Fixed In Build",
13
+ Proc.new { WTBuildHelpers::JIRA::print_help },
14
+ Proc.new { |args| WTBuildHelpers::JIRA::commandline_jira_update(args) }),
15
+
16
+ # TeamCity section
17
+ Command.new(
18
+ "teamcity_fetch_git_range",
19
+ "Get the git commit range for the last successful build on TeamCity to now",
20
+ Proc.new { WTBuildHelpers::TeamCity::print_help },
21
+ Proc.new { |args| WTBuildHelpers::TeamCity::commandline_fetch_commit_range(args) }),
22
+ ]
23
+
24
+ commands.unshift(
25
+ # Help
26
+ Command.new(
27
+ "help",
28
+ "Get help for a specific command",
29
+ Proc.new { WTBuildHelpers::Help::print_help },
30
+ Proc.new { |args| WTBuildHelpers::Help::print_command_help(args, commands) }),
31
+ )
32
+
33
+ command_name = ARGV.shift
34
+ if command_name.nil?
35
+ puts " Usage: #{__FILE__} command [options]"
36
+ puts
37
+ printf " %-30s %s\n", "Command", "Command Description"
38
+ puts
39
+ commands.each do |command|
40
+ printf " %-30s %s\n", command.command, command.description
41
+ end
42
+ else
43
+ command = commands.find do |c|
44
+ c.command == command_name
45
+ end
46
+
47
+ if command
48
+ if ARGV.count == 0 || ARGV[0] == "--help"
49
+ command.help_function.call()
50
+ else
51
+ command.run_function.call(ARGV)
52
+ end
53
+ else
54
+ puts "Unknown command: #{command_name}"
55
+ exit 1
56
+ end
57
+ end
@@ -1,8 +1,5 @@
1
1
  require "WTBuildHelpers/version"
2
2
 
3
- require "jira_tasks"
4
- require "teamcity_git_range_fetch"
5
-
6
- module WTBuildHelpers
7
- # Your code goes here...
8
- end
3
+ require "help"
4
+ require "jira"
5
+ require "teamcity"
@@ -1,3 +1,3 @@
1
1
  module WTBuildHelpers
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/help.rb ADDED
@@ -0,0 +1,25 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ module WTBuildHelpers
4
+ end
5
+
6
+ module WTBuildHelpers::Help
7
+
8
+ def self.print_help()
9
+ puts "Usage: wtbuild help command"
10
+ end
11
+
12
+ def self.print_command_help(args, commands)
13
+ command_name = args[0]
14
+ command = commands.find do |c|
15
+ c.command == command_name
16
+ end
17
+
18
+ if command
19
+ command.help_function.call()
20
+ else
21
+ puts "Unknown command: #{command_name}"
22
+ exit 1
23
+ end
24
+ end
25
+ end
data/lib/jira.rb ADDED
@@ -0,0 +1,241 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'pp'
5
+ require 'set'
6
+ require 'rest_client'
7
+ require 'base64'
8
+ require 'json'
9
+
10
+ module WTBuildHelpers
11
+ end
12
+
13
+ module WTBuildHelpers::JIRA
14
+ Options = Struct.new(:commit_range, :site, :username, :password, :build)
15
+
16
+ class JiraInfo
17
+ attr_reader :fixed_in_build_field_id
18
+ attr_reader :build_complete_transition_id
19
+
20
+ attr_reader :client
21
+
22
+ def initialize(site, username, password)
23
+ @site = site
24
+ @username = username
25
+ @password = password
26
+
27
+ auth_token = Base64.strict_encode64("#{username}:#{password}")
28
+ base_url = File.join(site, '/rest/api/2')
29
+ headers = { "Authorization" => "Basic #{auth_token}",
30
+ "Content-Type" => "application/json", }
31
+ @client = RestClient::Resource.new(base_url, :headers => headers)
32
+ end
33
+
34
+ def update_fixed_in_build()
35
+ # Find the Fixed In Build field
36
+ begin
37
+ response = @client['field'].get()
38
+
39
+ fields = JSON.parse(response.body);
40
+ fixed_field = fields.find do |field|
41
+ field["name"] == "Fixed In Build"
42
+ end
43
+
44
+ if fixed_field
45
+ @fixed_in_build_field_id = fixed_field["id"]
46
+ else
47
+ puts "Could not find 'Fixed In Build' field on supplied JIRA instance. Are you using WillowTree's JIRA? Does your user have access to the project?"
48
+ end
49
+ rescue RestClient::Exception => e
50
+ puts "Error code #{e.response.code} attempting to find Fixed In Build field"
51
+ end
52
+ end
53
+
54
+ def update_build_complete_transition(issue_key)
55
+ begin
56
+ fields_resource = "issue/#{issue_key}/transitions"
57
+
58
+ response = @client[fields_resource].get
59
+
60
+ response_json = JSON.parse(response.body)
61
+ transitions = response_json["transitions"]
62
+ build_complete_transition = transitions.find do |transition|
63
+ transition["name"] == "Build Complete"
64
+ end
65
+
66
+ if build_complete_transition
67
+ @build_complete_transition_id = build_complete_transition["id"]
68
+ end
69
+ rescue RestClient::Exception => e
70
+ puts "Error code #{e.response.code} attempting to find Build Complete transition"
71
+ end
72
+ end
73
+ end
74
+
75
+ class Parser
76
+ def self.parse(options)
77
+ args = Options.new()
78
+
79
+ opt_parser = OptionParser.new do |opts|
80
+ opts.banner = "Usage: jira_tasks.rb [options]"
81
+
82
+ opts.on("-c", "--commits COMMIT_RANGE", "Specify a commit range") do |commit_range|
83
+ args.commit_range = commit_range
84
+ end
85
+
86
+ opts.on("-u", "--username USERNAME", "Specify the user to connecto to JIRA") do |username|
87
+ args.username = username
88
+ end
89
+
90
+ opts.on("-p", "--password PASSWORD", "Specify the password to connect to JIRA") do |password|
91
+ args.password = password
92
+ end
93
+
94
+ opts.on("-s", "--site SITE", "Specify the base location of the JIRA instance") do |site|
95
+ site.slice!("http://")
96
+ site.insert(0, "https://") if not site.start_with?("https://")
97
+ args.site = site
98
+ end
99
+
100
+ opts.on("-b", "--build BUILD", "The build number for which this script is executing") do |build|
101
+ args.build = build
102
+ end
103
+
104
+ opts.on_tail("-h", "--help", "Show this message") do
105
+ puts opts
106
+ exit
107
+ end
108
+ end
109
+
110
+ opt_parser.parse!(options)
111
+ return args
112
+ end
113
+ end
114
+
115
+ def self.find_jira_issues(logs)
116
+ issue_set = Set.new
117
+ logs.each do |log|
118
+ issues = log.scan(/[A-Z][A-Z]+-[1-9][0-9]*/)
119
+ issue_set.merge(issues)
120
+ end
121
+
122
+ return issue_set
123
+ end
124
+
125
+ def self.get_git_history(commit_range)
126
+ happyface = "\u263A".encode('utf-8')
127
+ git_log = `git --no-pager log --format="%B#{happyface}" #{commit_range} `
128
+
129
+ #parse out the log
130
+ logs = git_log.split(happyface)
131
+
132
+ return logs
133
+ end
134
+
135
+ def self.jira_update(jira_info, issues, build)
136
+ if !jira_info.fixed_in_build_field_id
137
+ jira_info.update_fixed_in_build
138
+ end
139
+
140
+ # Something's wrong. We're not on our JIRA
141
+ return unless jira_info.fixed_in_build_field_id
142
+
143
+ issues.each do |issue_key|
144
+ #try to perform the transition
145
+ if !jira_info.build_complete_transition_id
146
+ jira_info.update_build_complete_transition(issue_key)
147
+ end
148
+
149
+ #still nil? Can't perform this transition
150
+ if !jira_info.build_complete_transition_id
151
+ puts "Could no update issue #{issue_key}: could not find transition"
152
+ next
153
+ end
154
+
155
+ params = {
156
+ transition: {
157
+ id: jira_info.build_complete_transition_id
158
+ },
159
+ fields: {
160
+ jira_info.fixed_in_build_field_id => build
161
+ }
162
+ }
163
+
164
+ puts "Updating issue #{issue_key}"
165
+ begin
166
+ issue_response = jira_info.client["issue/#{issue_key}/transitions"].post(JSON.generate(params))
167
+ rescue RestClient::Exception => e
168
+ puts "Error updating issue #{issue_key}: Error code #{e.response.code}"
169
+ end
170
+ end
171
+ end
172
+
173
+ def self.pull_travis_options(options)
174
+ puts "Pulling parameters from Travis-CI environment variables where needed...."
175
+ options.commit_range = ENV["TRAVIS_COMMIT_RANGE"] if options.commit_range == nil
176
+ options.build = ENV["TRAVIS_BUILD_NUMBER"] if options.build == nil
177
+ end
178
+
179
+ def self.pull_team_city_options(options)
180
+ puts "Pulling parameters from TeamCity environment variables where needed...."
181
+ options.commit_range = ENV["TEAMCITY_COMMIT_RANGE"] if options.commit_range == nil
182
+ options.build = ENV["BUILD_NUMBER"] if options.build == nil
183
+ end
184
+
185
+ def self.pull_common_options(options)
186
+ options.site = ENV["JIRA_URL"] if options.site == nil
187
+ options.username = ENV["JIRA_USERNAME"] if options.username == nil
188
+ options.password = ENV["JIRA_PASSWORD"] if options.password == nil
189
+ end
190
+
191
+ def self.validate_options(options)
192
+ valid_options = true
193
+ if options.commit_range == nil
194
+ puts "Commit range is required. None specified."
195
+ puts "You can specify this in TeamCity by running 'teamcity_git_range_fetch.rb' before this step." if ENV["TEAMCITY_VERSION"]
196
+ valid_options = false
197
+ end
198
+ if options.site == nil
199
+ puts "Site parameter is required, or use JIRA_URL environment variable on build servers"
200
+ valid_options = false
201
+ end
202
+ if options.username == nil
203
+ puts "No username for JIRA provided. Use JIRA_USERNAME environment variable on build servers"
204
+ valid_options = false
205
+ end
206
+ if options.password == nil
207
+ puts "No password for JIRA provided. Use JIRA_PASSWORD environment variable on build servers"
208
+ valid_options = false
209
+ end
210
+
211
+ valid_options
212
+ end
213
+
214
+ def self.print_help()
215
+ Parser.parse(["--help"])
216
+ end
217
+
218
+ def self.commandline_jira_update(args)
219
+ options = args ? Parser.parse(ARGV) : Options.new
220
+
221
+ if ENV["TRAVIS"]
222
+ pull_travis_options(options)
223
+ # Detect running on TeamCity for common prarams
224
+ elsif ENV["TEAMCITY_VERSION"]
225
+ pull_team_city_options(options)
226
+ end
227
+ pull_common_options(options)
228
+
229
+ if validate_options(options)
230
+ logs = get_git_history(options.commit_range)
231
+ issue_set = find_jira_issues(logs)
232
+
233
+ jira_info = JiraInfo.new options.site, options.username, options.password
234
+ jira_update(jira_info, issue_set, options.build)
235
+ end
236
+ end
237
+ end
238
+
239
+ if __FILE__ == $0
240
+ WTBuildHelpers::JIRA::commandline_jira_update(ARGV)
241
+ end
data/lib/teamcity.rb ADDED
@@ -0,0 +1,137 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'base64'
5
+ require 'json'
6
+ require 'rest_client'
7
+ require 'pp'
8
+ require 'uri'
9
+
10
+ module WTBuildHelpers
11
+ end
12
+
13
+ module WTBuildHelpers::TeamCity
14
+ Options = Struct.new(:url, :build_type_id, :username, :password, :export)
15
+
16
+ class OptionsParser
17
+ def self.parse(options)
18
+ args = Options.new()
19
+ args.export = false
20
+
21
+ opt_parser = OptionParser.new do |opts|
22
+ opts.banner = "Usage: #{__FILE__} [options]"
23
+
24
+ opts.on("-s", "--url URL", "Specify the URL for TeamCity") do |url|
25
+ url.slice!("http://")
26
+ url.insert(0, "http://")
27
+ args.url = url
28
+ end
29
+
30
+ opts.on("-u", "--username USERNAME", "Specify the user to connecto to TeamCity") do |username|
31
+ args.username = username
32
+ end
33
+
34
+ opts.on("-p", "--password PASSWORD", "Specify the password to connect to TeamCity") do |password|
35
+ args.password = password
36
+ end
37
+
38
+ opts.on("-t", "--build_type BUILD_TYPE", "The build_type_id for the running build from TeamCity") do |build_type_id|
39
+ args.build_type_id = build_type_id
40
+ end
41
+
42
+ opts.on("-e", "--export", "Output range for use as a parameter to the 'export' bash command") do
43
+ args.export = true
44
+ end
45
+
46
+ opts.on_tail("-h", "--help", "Show this message") do
47
+ puts opts
48
+ exit
49
+ end
50
+ end
51
+
52
+ opt_parser.parse!(options)
53
+ return args
54
+ end
55
+ end
56
+
57
+ def self.put_range(options, start_revision, end_revision)
58
+ value = ""
59
+ if end_revision
60
+ value = "#{end_revision}..#{start_revision}"
61
+ else
62
+ value = "#{start_revision}"
63
+ end
64
+
65
+ if options.export
66
+ puts value
67
+ else
68
+ puts "##teamcity[setParameter name='env.TEAMCITY_COMMIT_RANGE' value='#{value}']"
69
+ end
70
+ end
71
+
72
+ def self.get_last_successful_rev(options, branch)
73
+ auth = "#{options.username}:#{options.password}"
74
+ auth_token = Base64.strict_encode64(auth)
75
+
76
+ headers = { "Authorization" => "Basic #{auth_token}",
77
+ "Accept" => "application/json"
78
+ }
79
+
80
+ end_revision = nil
81
+ escaped_branch = URI.escape(branch)
82
+
83
+ full_url = "#{options.url}/app/rest/buildTypes/id:#{options.build_type_id}/builds/?locator=status:SUCCESS,branch:name:#{escaped_branch}"
84
+
85
+ response = RestClient.get(full_url, headers=headers)
86
+ json_response = JSON.parse(response.body)
87
+
88
+ if json_response["build"] && json_response["build"].count > 0
89
+ last_build = json_response["build"].first
90
+ last_build_id = last_build["id"]
91
+
92
+ build_full_url = "#{options.url}/app/rest/builds/id:#{last_build["id"]}"
93
+ build_response = RestClient.get(build_full_url, headers=headers)
94
+ build_json_response = JSON.parse(build_response.body)
95
+
96
+ end_revision = build_json_response["revisions"]["revision"][0]["version"]
97
+ end
98
+
99
+ return end_revision
100
+ end
101
+
102
+ def self.fetch_commit_range(options)
103
+ branch = `git rev-parse --abbrev-ref HEAD`
104
+ branch.strip!
105
+
106
+ start_revision = ENV["BUILD_VCS_NUMBER"]
107
+
108
+ begin
109
+ end_revision = get_last_successful_rev(options, branch)
110
+ if !end_revision
111
+ # Check refs/heads instead
112
+ branch = "refs/heads/#{branch}"
113
+ end_revision = get_last_successful_rev(options, branch)
114
+ end
115
+
116
+ put_range(options, start_revision, end_revision)
117
+ rescue RestClient::Exception => e
118
+ puts "ERROR getting last successful build from TeamCity:"
119
+ pp e
120
+ exit 1
121
+ end
122
+ end
123
+
124
+ def self.print_help()
125
+ OptionsParser.parse(["--help"])
126
+ end
127
+
128
+ def self.commandline_fetch_commit_range(args = nil)
129
+ options = OptionsParser.parse(args)
130
+
131
+ fetch_commit_range(options)
132
+ end
133
+ end
134
+
135
+ if __FILE__ == $0
136
+ WTBuildHelpers::TeamCity.commandline_fetch_commit_range(ARGV)
137
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: WTBuildHelpers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - WillowTreeApps
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-11-11 00:00:00.000000000 Z
11
+ date: 2016-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: OptionParser
@@ -70,8 +70,7 @@ description:
70
70
  email:
71
71
  - jeff.ward@willowtreeapps.com
72
72
  executables:
73
- - wtbuild_jira_update
74
- - wtbuild_teamcity_fetch_git_range
73
+ - wtbuild
75
74
  extensions: []
76
75
  extra_rdoc_files: []
77
76
  files:
@@ -83,11 +82,12 @@ files:
83
82
  - WTBuildHelpers.gemspec
84
83
  - bin/console
85
84
  - bin/setup
86
- - exe/wtbuild_jira_update
87
- - exe/wtbuild_teamcity_fetch_git_range
85
+ - exe/wtbuild
88
86
  - lib/WTBuildHelpers.rb
89
87
  - lib/WTBuildHelpers/version.rb
90
- - lib/jira_tasks.rb
88
+ - lib/help.rb
89
+ - lib/jira.rb
90
+ - lib/teamcity.rb
91
91
  - lib/teamcity_git_range_fetch.rb
92
92
  homepage:
93
93
  licenses:
@@ -1,5 +0,0 @@
1
- #! /usr/bin/env ruby
2
-
3
- require 'WTBuildHelpers'
4
-
5
- WTBuildHelpers::Jira.perform_jira_update(nil)
@@ -1,13 +0,0 @@
1
- #! /usr/bin/env ruby
2
-
3
- require 'WTBuildHelpers'
4
-
5
- if ARGV.length != 4
6
- puts "Usage: teamcity_git_range_fetch.rb [TeamCity Url] [TeamCity Build Type Id] [TeamCity User Name] [TeamCity Password]"
7
- puts "These parameters can be passed from TeamCity with the following line: "
8
- puts " teamcity_git_range_fetch.rb \"%teamcity.serverUrl%\" \"%system.teamcity.buildType.id%\" \"%system.teamcity.auth.userId%\" \"%system.teamcity.auth.password%\""
9
-
10
- exit
11
- end
12
-
13
- WTBuildHelpers::TeamCity.fetch_commit_range(ARGV[0], ARGV[1], ARGV[2], ARGV[3])
data/lib/jira_tasks.rb DELETED
@@ -1,245 +0,0 @@
1
- #! /usr/bin/env ruby
2
-
3
- require 'optparse'
4
- require 'pp'
5
- require 'set'
6
- require 'rest_client'
7
- require 'base64'
8
- require 'json'
9
-
10
- module WTBuildHelpers
11
- module Jira
12
- Options = Struct.new(:commit_range, :site, :username, :password, :build)
13
-
14
- class JiraInfo
15
- attr_reader :fixed_in_build_field_id
16
- attr_reader :build_complete_transition_id
17
-
18
- attr_reader :site
19
- attr_reader :base_url
20
- attr_reader :auth_token
21
-
22
- def initialize(site, username, password)
23
- @site = site
24
- @username = username
25
- @password = password
26
-
27
- @auth_token = Base64.encode64("#{username}:#{password}")
28
- @base_url = File.join(site, '/rest/api/2')
29
- end
30
-
31
- def update_fixed_in_build()
32
- headers = { "Authorization" => "Basic #{@auth_token}" }
33
-
34
- # Find the Fixed In Build field
35
- begin
36
- fields_url = File.join(@base_url, 'field')
37
- response = RestClient.get(fields_url, headers=headers)
38
-
39
- fields = JSON.parse(response.body);
40
- fixed_field = fields.find do |field|
41
- field["name"] == "Fixed In Build"
42
- end
43
-
44
- if not fixed_field.nil?
45
- @fixed_in_build_field_id = fixed_field["id"]
46
- else
47
- puts "Could not find 'Fixed In Build' field on supplied JIRA instance. Are you using WillowTree's JIRA? Does your user have access to the project?"
48
- end
49
- rescue RestClient::Exception => e
50
- puts "Error code #{e.response.code} attempting to find Fixed In Build field"
51
- end
52
- end
53
-
54
- def update_build_complete_transition(issue_key)
55
- headers = { "Authorization" => "Basic #{@auth_token}"}
56
-
57
- fields_url = File.join(@base_url, 'issue', issue_key, 'transitions')
58
- begin
59
- response = RestClient.get(fields_url, headers=headers)
60
-
61
- response_json = JSON.parse(response.body)
62
- transitions = response_json["transitions"]
63
- build_complete_transition = transitions.find do |transition|
64
- transition["name"] == "Build Complete"
65
- end
66
-
67
- if not build_complete_transition.nil?
68
- @build_complete_transition_id = build_complete_transition["id"]
69
- end
70
- rescue RestClient::Exception => e
71
- puts "Error code #{e.response.code} attempting to find Build Complete transition"
72
- end
73
- end
74
- end
75
-
76
- class Parser
77
- def self.parse(options)
78
- args = Options.new()
79
-
80
- opt_parser = OptionParser.new do |opts|
81
- opts.banner = "Usage: jira_tasks.rb [options]"
82
-
83
- opts.on("-c", "--commits COMMIT_RANGE", "Specify a commit range") do |commit_range|
84
- args.commit_range = commit_range
85
- end
86
-
87
- opts.on("-u", "--username USERNAME", "Specify the user to connecto to JIRA") do |username|
88
- args.username = username
89
- end
90
-
91
- opts.on("-p", "--password PASSWORD", "Specify the password to connect to JIRA") do |password|
92
- args.password = password
93
- end
94
-
95
- opts.on("-s", "--site SITE", "Specify the base location of the JIRA instance") do |site|
96
- site.slice!("http://")
97
- site.insert(0, "https://")
98
- args.site = site
99
- end
100
-
101
- opts.on("-b", "--build BUILD", "The build number for which this script is executing") do |build|
102
- args.build = build
103
- end
104
-
105
- opts.on_tail("-h", "--help", "Show this message") do
106
- puts opts
107
- exit
108
- end
109
- end
110
-
111
- opt_parser.parse!(options)
112
- return args
113
- end
114
- end
115
-
116
- def self.find_jira_issues(logs)
117
- issue_set = Set.new
118
- logs.each do |log|
119
- issues = log.scan(/[A-Z][A-Z]+-[1-9][0-9]*/)
120
- issue_set.merge(issues)
121
- end
122
-
123
- return issue_set
124
- end
125
-
126
- def self.get_git_history(commit_range)
127
- happyface = "\u263A".encode('utf-8')
128
- git_log = `git --no-pager log --format="%B#{happyface}" #{commit_range} `
129
-
130
- #parse out the log
131
- logs = git_log.split(happyface)
132
-
133
- return logs
134
- end
135
-
136
- def self.jira_update(jira_info, issues, build)
137
- jira_url = File.join(jira_info.base_url, 'issue')
138
-
139
- if jira_info.fixed_in_build_field_id.nil?
140
- jira_info.update_fixed_in_build
141
- end
142
-
143
- # Something's wrong. We're not on our JIRA
144
- return if jira_info.fixed_in_build_field_id.nil?
145
-
146
- headers = { "Authorization" => "Basic #{jira_info.auth_token}",
147
- "Content-Type" => "application/json" }
148
-
149
- issues.each do |issue_key|
150
- #try to perform the transition
151
- if jira_info.build_complete_transition_id.nil?
152
- jira_info.update_build_complete_transition(issue_key)
153
- end
154
-
155
- #still nil? Can't perform this transition
156
- if jira_info.build_complete_transition_id.nil?
157
- puts "Could no update issue #{issue_key}: could not find transition"
158
- next
159
- end
160
-
161
- params = {
162
- transition: {
163
- id: jira_info.build_complete_transition_id
164
- },
165
- fields: {
166
- jira_info.fixed_in_build_field_id => build
167
- }
168
- }
169
- transition_url = File.join(jira_url, issue_key, 'transitions')
170
-
171
- puts "Updating issue #{issue_key}"
172
- begin
173
- issue_response = RestClient.post(transition_url, JSON.generate(params), headers=headers)
174
- rescue RestClient::Exception => e
175
- puts "Error updating issue #{issue_key}: Error code #{e.response.code}"
176
- end
177
- end
178
- end
179
-
180
- def self.pull_travis_options(options)
181
- puts "Pulling parameters from Travis-CI environment variables where needed...."
182
- options.commit_range = ENV["TRAVIS_COMMIT_RANGE"] if options.commit_range == nil
183
- options.build = ENV["TRAVIS_BUILD_NUMBER"] if options.build == nil
184
- end
185
-
186
- def self.pull_team_city_options(options)
187
- puts "Pulling parameters from TeamCity environment variables where needed...."
188
- options.commit_range = ENV["TEAMCITY_COMMIT_RANGE"] if options.commit_range == nil
189
- options.build = ENV["BUILD_NUMBER"] if options.build == nil
190
- end
191
-
192
- def self.pull_common_options(options)
193
- options.site = ENV["JIRA_URL"] if options.site == nil
194
- options.username = ENV["JIRA_USERNAME"] if options.username == nil
195
- options.password = ENV["JIRA_PASSWORD"] if options.password == nil
196
- end
197
-
198
- def self.validate_options(options)
199
- valid_options = true
200
- if options.commit_range == nil
201
- puts "Commit range is required. None specified."
202
- puts "You can specify this in TeamCity by running 'teamcity_git_range_fetch.rb' before this step." if ENV["TEAMCITY_VERSION"]
203
- valid_options = false
204
- end
205
- if options.site == nil
206
- puts "Site parameter is required, or use JIRA_URL environment variable on build servers"
207
- valid_options = false
208
- end
209
- if options.username == nil
210
- puts "No username for JIRA provided. Use JIRA_USERNAME environment variable on build servers"
211
- valid_options = false
212
- end
213
- if options.password == nil
214
- puts "No password for JIRA provided. Use JIRA_PASSWORD environment variable on build servers"
215
- valid_options = false
216
- end
217
-
218
- valid_options
219
- end
220
-
221
- def self.perform_jira_update(options = nil)
222
- options = Options.new if options == nil
223
- if ENV["TRAVIS"]
224
- pull_travis_options(options)
225
- # Detect running on TeamCity for common prarams
226
- elsif ENV["TEAMCITY_VERSION"]
227
- pull_team_city_options(options)
228
- end
229
- pull_common_options(options)
230
-
231
- if validate_options(options)
232
- logs = get_git_history(options.commit_range)
233
- issue_set = find_jira_issues(logs)
234
-
235
- jira_info = JiraInfo.new options.site, options.username, options.password
236
- jira_update(jira_info, issue_set, options.build)
237
- end
238
- end
239
- end
240
- end
241
-
242
- if __FILE__ == $0
243
- options = WTBuildHelpers::Jira::Parser.parse(ARGV)
244
- WTBuildHelpers::Jira.perform_jira_update(options)
245
- end