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