fogbugz 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/fogbugz +78 -0
- data/bin/fogbugz-areas +64 -0
- data/bin/fogbugz-assign +58 -0
- data/bin/fogbugz-categories +55 -0
- data/bin/fogbugz-close +57 -0
- data/bin/fogbugz-edit +281 -0
- data/bin/fogbugz-filter +78 -0
- data/bin/fogbugz-filters +53 -0
- data/bin/fogbugz-list +69 -0
- data/bin/fogbugz-login +60 -0
- data/bin/fogbugz-logoff +49 -0
- data/bin/fogbugz-milestones +57 -0
- data/bin/fogbugz-new +165 -0
- data/bin/fogbugz-people +57 -0
- data/bin/fogbugz-priorities +55 -0
- data/bin/fogbugz-projects +57 -0
- data/bin/fogbugz-reactivate +56 -0
- data/bin/fogbugz-reopen +56 -0
- data/bin/fogbugz-resolve +82 -0
- data/bin/fogbugz-show +147 -0
- data/bin/fogbugz-start +55 -0
- data/bin/fogbugz-statuses +62 -0
- data/bin/fogbugz-stop +50 -0
- data/lib/dummy.rb +1 -0
- metadata +119 -0
data/bin/fogbugz
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
basename = File.basename(__FILE__)
|
4
|
+
absolute = File.expand_path(__FILE__)
|
5
|
+
|
6
|
+
usage = <<HERE
|
7
|
+
usage: #{basename} <subcommand> [options]
|
8
|
+
#{basename} <case>
|
9
|
+
|
10
|
+
The subcommands are:
|
11
|
+
areas List active areas.
|
12
|
+
assign Assign a case.
|
13
|
+
categories List available categories.
|
14
|
+
close Close a resolved case.
|
15
|
+
edit Edit an existing case.
|
16
|
+
filter Set the current default filter.
|
17
|
+
filters List available filters.
|
18
|
+
list List cases by the current filter or a search query.
|
19
|
+
login Create an API token.
|
20
|
+
logoff Invalidate an API token.
|
21
|
+
milestones List active milestones.
|
22
|
+
new Create a new case.
|
23
|
+
people List active users.
|
24
|
+
priorities List available priorities.
|
25
|
+
projects List active projects.
|
26
|
+
reactivate Reactivate a resolved case.
|
27
|
+
reopen Reopen a closed case.
|
28
|
+
resolve Resolve a case.
|
29
|
+
show Show details on a case.
|
30
|
+
start Start working on a case.
|
31
|
+
statuses List available statuses.
|
32
|
+
stop Stop working on a case.
|
33
|
+
|
34
|
+
See '#{basename} help <commands>' for more information on a \
|
35
|
+
specific command.
|
36
|
+
HERE
|
37
|
+
|
38
|
+
if ARGV.length == 0
|
39
|
+
puts usage
|
40
|
+
exit 1
|
41
|
+
end
|
42
|
+
|
43
|
+
case ARGV[0]
|
44
|
+
when 'help'
|
45
|
+
if ARGV.length == 1
|
46
|
+
puts usage
|
47
|
+
exit 1
|
48
|
+
else
|
49
|
+
case ARGV[1]
|
50
|
+
when 'areas', 'assign', 'categories', 'close', 'edit', 'filter', 'filters',
|
51
|
+
'list', 'login', 'logoff', 'milestones', 'new', 'people', 'priorities',
|
52
|
+
'projects', 'reactivate', 'reopen', 'resolve', 'show', 'start',
|
53
|
+
'statuses', 'stop'
|
54
|
+
subcommand = ARGV[1]
|
55
|
+
ARGV.clear
|
56
|
+
ARGV.push '--help'
|
57
|
+
load "#{absolute}-#{subcommand}"
|
58
|
+
exit 0
|
59
|
+
else
|
60
|
+
puts usage
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
when 'areas', 'assign', 'categories', 'close', 'edit', 'filter', 'filters',
|
65
|
+
'list', 'login', 'logoff', 'milestones', 'new', 'people', 'priorities',
|
66
|
+
'projects', 'reactivate', 'reopen', 'resolve', 'show', 'start',
|
67
|
+
'statuses', 'stop'
|
68
|
+
load "#{absolute}-#{ARGV.shift}"
|
69
|
+
exit 0
|
70
|
+
else
|
71
|
+
if ARGV[0].to_i.to_s == ARGV[0]
|
72
|
+
load "#{absolute}-show"
|
73
|
+
exit 0
|
74
|
+
else
|
75
|
+
puts usage
|
76
|
+
exit 1
|
77
|
+
end
|
78
|
+
end
|
data/bin/fogbugz-areas
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'typhoeus'
|
5
|
+
require 'xml'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
api_url = ENV['FOGBUGZ_API_URL']
|
9
|
+
unless api_url
|
10
|
+
puts "Environment variable FOGBUGZ_API_URL must be set."
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
api_token = ENV['FOGBUGZ_API_TOKEN']
|
15
|
+
unless api_url
|
16
|
+
puts "Environment variable FOGBUGZ_API_TOKEN must be set."
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
options = {}
|
21
|
+
optparse = OptionParser.new do |opts|
|
22
|
+
opts.banner = "usage: #{File::basename(__FILE__)} [options]"
|
23
|
+
|
24
|
+
options[:verbose] = false
|
25
|
+
opts.on('-v', '--verbose', 'Output verbose debugging information') do
|
26
|
+
options[:verbose] = true
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on_tail('-h', '--help') do
|
30
|
+
puts optparse.help
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
options[:project] = nil
|
35
|
+
opts.on('--project=<project>', 'Filter by project.') do |project|
|
36
|
+
options[:project] = project
|
37
|
+
end
|
38
|
+
end
|
39
|
+
optparse.parse!
|
40
|
+
|
41
|
+
response = Typhoeus::Request.get(api_url,
|
42
|
+
:verbose => options[:verbose],
|
43
|
+
:params => {
|
44
|
+
:cmd => 'listAreas',
|
45
|
+
:token => api_token,
|
46
|
+
:ixProject => options[:project] })
|
47
|
+
if response.code != 200
|
48
|
+
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
49
|
+
exit 1
|
50
|
+
end
|
51
|
+
|
52
|
+
result = XML::Parser.string(response.body).parse
|
53
|
+
error = result.find_first('/response/error')
|
54
|
+
if error
|
55
|
+
puts "Failed with error: #{error.content}."
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
|
59
|
+
result.find('/response/areas/area').each do |status|
|
60
|
+
puts format("%-20.20s %-20.20s %s\n",
|
61
|
+
status.find_first('sPersonOwner').content,
|
62
|
+
status.find_first('sProject').content,
|
63
|
+
status.find_first('sArea').content).strip!
|
64
|
+
end
|
data/bin/fogbugz-assign
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'typhoeus'
|
5
|
+
require 'xml'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
api_url = ENV['FOGBUGZ_API_URL']
|
9
|
+
unless api_url
|
10
|
+
puts "Environment variable FOGBUGZ_API_URL must be set."
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
api_token = ENV['FOGBUGZ_API_TOKEN']
|
15
|
+
unless api_url
|
16
|
+
puts "Environment variable FOGBUGZ_API_TOKEN must be set."
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
options = {}
|
21
|
+
optparse = OptionParser.new do |opts|
|
22
|
+
opts.banner = "usage: #{File::basename(__FILE__)} [options] <case> <assignee>"
|
23
|
+
|
24
|
+
options[:verbose] = false
|
25
|
+
opts.on('-v', '--verbose', 'Output verbose debugging information.') do
|
26
|
+
options[:verbose] = true
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on_tail('-h', '--help') do
|
30
|
+
puts optparse.help
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
optparse.parse!
|
35
|
+
|
36
|
+
unless ARGV.length == 2
|
37
|
+
puts optparse.help
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
|
41
|
+
response = Typhoeus::Request.get(api_url,
|
42
|
+
:verbose => options[:verbose],
|
43
|
+
:params => {
|
44
|
+
:cmd => 'assign',
|
45
|
+
:token => api_token,
|
46
|
+
:ixBug => ARGV[0],
|
47
|
+
:sPersonAssignedTo => ARGV[1] })
|
48
|
+
if response.code != 200
|
49
|
+
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
50
|
+
exit 1
|
51
|
+
end
|
52
|
+
|
53
|
+
result = XML::Parser.string(response.body).parse
|
54
|
+
error = result.find_first('/response/error')
|
55
|
+
if error
|
56
|
+
puts "Failed with error: #{error.content}."
|
57
|
+
exit 1
|
58
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'typhoeus'
|
5
|
+
require 'xml'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
api_url = ENV['FOGBUGZ_API_URL']
|
9
|
+
unless api_url
|
10
|
+
puts "Environment variable FOGBUGZ_API_URL must be set."
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
api_token = ENV['FOGBUGZ_API_TOKEN']
|
15
|
+
unless api_url
|
16
|
+
puts "Environment variable FOGBUGZ_API_TOKEN must be set."
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
options = {}
|
21
|
+
optparse = OptionParser.new do |opts|
|
22
|
+
opts.banner = "usage: #{File::basename(__FILE__)} [options]"
|
23
|
+
|
24
|
+
options[:verbose] = false
|
25
|
+
opts.on('-v', '--verbose', 'Output verbose debugging information') do
|
26
|
+
options[:verbose] = true
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on_tail('-h', '--help') do
|
30
|
+
puts optparse.help
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
optparse.parse!
|
35
|
+
|
36
|
+
response = Typhoeus::Request.get(api_url,
|
37
|
+
:verbose => options[:verbose],
|
38
|
+
:params => {
|
39
|
+
:cmd => 'listCategories',
|
40
|
+
:token => api_token })
|
41
|
+
if response.code != 200
|
42
|
+
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
43
|
+
exit 1
|
44
|
+
end
|
45
|
+
|
46
|
+
result = XML::Parser.string(response.body).parse
|
47
|
+
error = result.find_first('/response/error')
|
48
|
+
if error
|
49
|
+
puts "Failed with error: #{error.content}."
|
50
|
+
exit 1
|
51
|
+
end
|
52
|
+
|
53
|
+
result.find('/response/categories/category').each do |status|
|
54
|
+
puts status.find_first('sCategory').content
|
55
|
+
end
|
data/bin/fogbugz-close
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'typhoeus'
|
5
|
+
require 'xml'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
api_url = ENV['FOGBUGZ_API_URL']
|
9
|
+
unless api_url
|
10
|
+
puts "Environment variable FOGBUGZ_API_URL must be set."
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
api_token = ENV['FOGBUGZ_API_TOKEN']
|
15
|
+
unless api_url
|
16
|
+
puts "Environment variable FOGBUGZ_API_TOKEN must be set."
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
options = {}
|
21
|
+
optparse = OptionParser.new do |opts|
|
22
|
+
opts.banner = "usage: #{File::basename(__FILE__)} [options] <case>"
|
23
|
+
|
24
|
+
options[:verbose] = false
|
25
|
+
opts.on('-v', '--verbose', 'Output verbose debugging information.') do
|
26
|
+
options[:verbose] = true
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on_tail('-h', '--help') do
|
30
|
+
puts optparse.help
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
optparse.parse!
|
35
|
+
|
36
|
+
unless ARGV[0]
|
37
|
+
puts optparse.help
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
|
41
|
+
response = Typhoeus::Request.get(api_url,
|
42
|
+
:verbose => options[:verbose],
|
43
|
+
:params => {
|
44
|
+
:cmd => 'listAreas',
|
45
|
+
:token => api_token,
|
46
|
+
:ixBug => ARGV[0] })
|
47
|
+
if response.code != 200
|
48
|
+
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
49
|
+
exit 1
|
50
|
+
end
|
51
|
+
|
52
|
+
result = XML::Parser.string(response.body).parse
|
53
|
+
error = result.find_first('/response/error')
|
54
|
+
if error
|
55
|
+
puts "Failed with error: #{error.content}."
|
56
|
+
exit 1
|
57
|
+
end
|
data/bin/fogbugz-edit
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'typhoeus'
|
5
|
+
require 'xml'
|
6
|
+
require 'tempfile'
|
7
|
+
require 'optparse'
|
8
|
+
require 'yaml'
|
9
|
+
require 'English'
|
10
|
+
|
11
|
+
api_url = ENV['FOGBUGZ_API_URL']
|
12
|
+
unless api_url
|
13
|
+
puts "Environment variable FOGBUGZ_API_URL must be set."
|
14
|
+
exit 1
|
15
|
+
end
|
16
|
+
|
17
|
+
api_token = ENV['FOGBUGZ_API_TOKEN']
|
18
|
+
unless api_url
|
19
|
+
puts "Environment variable FOGBUGZ_API_TOKEN must be set."
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
|
23
|
+
editor = ENV['EDITOR'] || 'vim'
|
24
|
+
|
25
|
+
options = {}
|
26
|
+
optparse = OptionParser.new do |opts|
|
27
|
+
opts.banner = "usage: #{File::basename(__FILE__)} [options] <case>"
|
28
|
+
|
29
|
+
options[:verbose] = false
|
30
|
+
opts.on('-v', '--verbose', 'Output verbose debugging information') do
|
31
|
+
options[:verbose] = true
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on_tail('-h', '--help') do
|
35
|
+
puts optparse.help
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
|
39
|
+
options[:file] = nil
|
40
|
+
opts.on('-f', '--file=<file>',
|
41
|
+
'Take the case content from the given file. Use - to read from STDIN.') do |file|
|
42
|
+
options[:file] = file
|
43
|
+
end
|
44
|
+
|
45
|
+
options[:template] = nil
|
46
|
+
opts.on('-t', '--template=<template>',
|
47
|
+
'Use the file content or - for STDIN as the initial case content.') do |template|
|
48
|
+
options[:template] = template
|
49
|
+
end
|
50
|
+
end
|
51
|
+
optparse.parse!
|
52
|
+
|
53
|
+
unless ARGV.length == 1
|
54
|
+
puts optparse.help
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
case_id = ARGV[0]
|
58
|
+
|
59
|
+
response = Typhoeus::Request.get(api_url,
|
60
|
+
:verbose => options[:verbose],
|
61
|
+
:params => {
|
62
|
+
:cmd => 'search',
|
63
|
+
:token => api_token,
|
64
|
+
:cols => 'ixBug,ixBugParent,tags,sTitle,sProject,sArea,sFixFor,sCategory,sPersonAssignedTo,sPriority,hrsCurrEst,events',
|
65
|
+
:q => case_id })
|
66
|
+
if response.code != 200
|
67
|
+
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
68
|
+
exit 1
|
69
|
+
end
|
70
|
+
|
71
|
+
result = XML::Parser.string(response.body).parse
|
72
|
+
error = result.find_first('/response/error')
|
73
|
+
if error
|
74
|
+
puts "Failed with error: #{error.content}."
|
75
|
+
exit 1
|
76
|
+
end
|
77
|
+
|
78
|
+
bug = result.find_first("/response/cases/case[@ixBug='#{case_id}']")
|
79
|
+
unless bug
|
80
|
+
puts "Case #{case_id} does not exist."
|
81
|
+
exit 1
|
82
|
+
end
|
83
|
+
|
84
|
+
invoke_editor = true
|
85
|
+
if options[:file]
|
86
|
+
if options[:file] == '-'
|
87
|
+
ARGV.replace []
|
88
|
+
else
|
89
|
+
ARGV.replace [options[:file]]
|
90
|
+
end
|
91
|
+
template = ARGF.read
|
92
|
+
invoke_editor = false
|
93
|
+
elsif options[:template]
|
94
|
+
if options[:template] == '-'
|
95
|
+
ARGV.replace []
|
96
|
+
else
|
97
|
+
ARGV.replace [options[:template]]
|
98
|
+
end
|
99
|
+
template = ARGF.read
|
100
|
+
else
|
101
|
+
template = ''
|
102
|
+
template << <<HERE
|
103
|
+
# Fill in metadata for the case.
|
104
|
+
HERE
|
105
|
+
|
106
|
+
sTitle = bug.find_first('sTitle')
|
107
|
+
if sTitle and sTitle.content and not sTitle.content.empty?
|
108
|
+
template << "# title: #{sTitle.content}\n"
|
109
|
+
else
|
110
|
+
template << "# title: <title>\n"
|
111
|
+
end
|
112
|
+
|
113
|
+
sPersonAssignedTo = bug.find_first('sPersonAssignedTo')
|
114
|
+
if sPersonAssignedTo and sPersonAssignedTo.content and
|
115
|
+
not sPersonAssignedTo.content.empty?
|
116
|
+
template << "# assignee: #{sPersonAssignedTo.content}\n"
|
117
|
+
else
|
118
|
+
template << "# assignee: <person>\n"
|
119
|
+
end
|
120
|
+
|
121
|
+
ixBugParent = bug.find_first('ixBugParent')
|
122
|
+
if ixBugParent and ixBugParent.content and not ixBugParent.content.empty? and
|
123
|
+
ixBugParent.content != '0'
|
124
|
+
template << "# parent: #{ixBugParent.content}\n"
|
125
|
+
else
|
126
|
+
template << "# parent: <case>\n"
|
127
|
+
end
|
128
|
+
|
129
|
+
tags = bug.find('tags/tag').collect { |tag| tag.content }
|
130
|
+
if tags and not tags.empty?
|
131
|
+
template << "# tags: [#{tags.join(', ')}]\n"
|
132
|
+
else
|
133
|
+
template << "# tags: [bug, enhancement]\n"
|
134
|
+
end
|
135
|
+
|
136
|
+
sProject = bug.find_first('sProject')
|
137
|
+
if sProject and sProject.content and not sProject.content.empty?
|
138
|
+
template << "# project: #{sProject.content}\n"
|
139
|
+
else
|
140
|
+
template << "# project: <project>\n"
|
141
|
+
end
|
142
|
+
|
143
|
+
sArea = bug.find_first('sArea')
|
144
|
+
if sArea and sArea.content and not sArea.content.empty?
|
145
|
+
template << "# area: #{sArea.content}\n"
|
146
|
+
else
|
147
|
+
template << "# area: <area>\n"
|
148
|
+
end
|
149
|
+
|
150
|
+
sFixFor = bug.find_first('sFixFor')
|
151
|
+
if sFixFor and sFixFor.content and not sFixFor.content.empty?
|
152
|
+
template << "# milestone: #{sFixFor.content}\n"
|
153
|
+
else
|
154
|
+
template << "# milestone: <milestone>"
|
155
|
+
end
|
156
|
+
|
157
|
+
sCategory = bug.find_first('sCategory')
|
158
|
+
if sCategory and sCategory.content and not sCategory.content.empty?
|
159
|
+
template << "# category: #{sCategory.content}\n"
|
160
|
+
else
|
161
|
+
template << "# category: <category>"
|
162
|
+
end
|
163
|
+
|
164
|
+
sPriority = bug.find_first('sPriority')
|
165
|
+
if sPriority and sPriority.content and not sPriority.content.empty?
|
166
|
+
template << "# priority: #{sPriority.content}\n"
|
167
|
+
else
|
168
|
+
template << "# priority: <priority>"
|
169
|
+
end
|
170
|
+
|
171
|
+
hrsCurrEst = bug.find_first('hrsCurrEst')
|
172
|
+
if hrsCurrEst and hrsCurrEst.content and not hrsCurrEst.content.empty? and
|
173
|
+
hrsCurrEst.content != '0'
|
174
|
+
template << "# estimate: #{hrsCurrEst.content}\n"
|
175
|
+
else
|
176
|
+
template << "# estimate: <estimate>\n"
|
177
|
+
end
|
178
|
+
|
179
|
+
template << "\n"
|
180
|
+
bug.find('events/event').each do |event|
|
181
|
+
evtDescription = event.find_first('evtDescription')
|
182
|
+
if evtDescription and evtDescription.content and
|
183
|
+
not evtDescription.content.empty?
|
184
|
+
time = Time.parse(event.find_first('dt').content).localtime
|
185
|
+
template << format("# %s at %s on %s.\n", evtDescription.content,
|
186
|
+
time.strftime('%-l:%M %p'),
|
187
|
+
time.strftime('%A, %B %e %Y'))
|
188
|
+
end
|
189
|
+
|
190
|
+
sChanges = event.find_first('sChanges')
|
191
|
+
if sChanges and sChanges.content and not sChanges.content.empty?
|
192
|
+
commented = sChanges.content.gsub(/\n/, "\n# ")
|
193
|
+
template << "# #{commented}\n"
|
194
|
+
end
|
195
|
+
|
196
|
+
summary = event.find_first('s')
|
197
|
+
if summary and summary.content and not summary.content.empty?
|
198
|
+
commented = summary.content.gsub(/\n/, "\n# ")
|
199
|
+
template << "# #{commented}\n"
|
200
|
+
end
|
201
|
+
template << "\n"
|
202
|
+
end
|
203
|
+
template << <<HERE
|
204
|
+
# Enter an additional note for the case after the dashes.
|
205
|
+
---
|
206
|
+
HERE
|
207
|
+
end
|
208
|
+
|
209
|
+
content = template
|
210
|
+
if invoke_editor
|
211
|
+
tempfile = Tempfile.new ['case', '.md']
|
212
|
+
tempfile.write template
|
213
|
+
tempfile.close
|
214
|
+
rc = system "#{editor} #{tempfile.path}"
|
215
|
+
unless rc
|
216
|
+
puts "Editor exited with non-zero status. Aborting."
|
217
|
+
exit 1
|
218
|
+
end
|
219
|
+
tempfile.open
|
220
|
+
content = tempfile.read
|
221
|
+
tempfile.close
|
222
|
+
tempfile.delete
|
223
|
+
end
|
224
|
+
|
225
|
+
data = {}
|
226
|
+
if content =~ /(.*?\n?)^(---\s*$\n?)/m
|
227
|
+
# Combined YAML front matter with text content.
|
228
|
+
begin
|
229
|
+
data = YAML.load($1) || {}
|
230
|
+
data['body'] = $POSTMATCH if $POSTMATCH and not $POSTMATCH.empty?
|
231
|
+
rescue => e
|
232
|
+
puts "Exception reading YAML front matter. #{e.inspect}"
|
233
|
+
exit 1
|
234
|
+
end
|
235
|
+
else
|
236
|
+
begin
|
237
|
+
# YAML only content.
|
238
|
+
data = YAML.load(content)
|
239
|
+
was_yaml = true if data and not data.empty?
|
240
|
+
if data.instance_of? String
|
241
|
+
# Text only content.
|
242
|
+
data = { 'body' => data }
|
243
|
+
end
|
244
|
+
rescue => e
|
245
|
+
data = {}
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
if not data or data.empty?
|
250
|
+
puts "No changes to case. Aborting."
|
251
|
+
exit 1
|
252
|
+
end
|
253
|
+
|
254
|
+
response = Typhoeus::Request.get(api_url,
|
255
|
+
:verbose => options[:verbose],
|
256
|
+
:params => {
|
257
|
+
:cmd => 'edit',
|
258
|
+
:token => api_token,
|
259
|
+
:ixBug => case_id,
|
260
|
+
:sTitle => data['title'],
|
261
|
+
:ixBugParent => data['parent'],
|
262
|
+
:sTags => data['tags'] ? data['tags'].join(',') : nil,
|
263
|
+
:sProject => data['project'],
|
264
|
+
:sArea => data['area'],
|
265
|
+
:sFixFor => data['milestone'],
|
266
|
+
:sCategory => data['category'],
|
267
|
+
:sPersonAssignedTo => data['assignee'],
|
268
|
+
:sPriority => data['priority'],
|
269
|
+
:hrsCurrEst => data['estimate'],
|
270
|
+
:sEvent => data['body'] })
|
271
|
+
if response.code != 200
|
272
|
+
puts "HTTP request to #{api_url} failed with code #{response.code}."
|
273
|
+
exit 1
|
274
|
+
end
|
275
|
+
|
276
|
+
result = XML::Parser.string(response.body).parse
|
277
|
+
error = result.find_first('/response/error')
|
278
|
+
if error
|
279
|
+
puts "Failed with error: #{error.content}."
|
280
|
+
exit 1
|
281
|
+
end
|