WTBuildHelpers 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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