schubert-minglr 0.1.0.3 → 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -0
- data/Manifest.txt +22 -0
- data/PostInstall.txt +12 -0
- data/README.rdoc +3 -2
- data/Rakefile +1 -1
- data/lib/minglr.rb +24 -0
- data/lib/minglr/attachment.rb +7 -0
- data/lib/minglr/card.rb +2 -0
- data/lib/minglr/input_cache.rb +22 -0
- data/lib/minglr/mingle_resource.rb +5 -0
- data/lib/minglr/minglr_action.rb +200 -0
- data/lib/minglr/minglr_config_parser.rb +44 -0
- data/lib/minglr/minglr_options_parser.rb +70 -0
- data/lib/minglr/mtx_options_parser.rb +53 -0
- data/lib/minglr/property_definition.rb +2 -0
- data/lib/minglr/transition_execution.rb +2 -0
- data/lib/minglr/user.rb +2 -0
- data/lib/version.rb +1 -0
- data/tasks/commit.sample.rake +86 -0
- data/tasks/svn.sample.rake +27 -0
- metadata +80 -20
- data/minglr.gemspec +0 -14
- data/minglrconfig.sample +0 -18
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
PostInstall.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
bin/mtx
|
7
|
+
bin/minglr
|
8
|
+
lib/minglr.rb
|
9
|
+
lib/version.rb
|
10
|
+
lib/minglr/attachment.rb
|
11
|
+
lib/minglr/card.rb
|
12
|
+
lib/minglr/input_cache.rb
|
13
|
+
lib/minglr/mingle_resource.rb
|
14
|
+
lib/minglr/minglr_action.rb
|
15
|
+
lib/minglr/minglr_config_parser.rb
|
16
|
+
lib/minglr/minglr_options_parser.rb
|
17
|
+
lib/minglr/mtx_options_parser.rb
|
18
|
+
lib/minglr/property_definition.rb
|
19
|
+
lib/minglr/transition_execution.rb
|
20
|
+
lib/minglr/user.rb
|
21
|
+
tasks/commit.sample.rake
|
22
|
+
tasks/svn.sample.rake
|
data/PostInstall.txt
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
For more information:
|
3
|
+
on minglr, see http://github.com/schubert/minglr/tree/master
|
4
|
+
on Mingle, see http://mingle.thoughtworks.com/mingle-agile-project-management
|
5
|
+
on the authors (Stephen Chu), see http://www.stephenchu.com
|
6
|
+
(Michael Schubert), see http://schubert.cx/
|
7
|
+
|
8
|
+
======================================================================================================
|
9
|
+
= Don't not forget to enable your Mingle API authentication by configuring the auth_config.yml file! =
|
10
|
+
======================================================================================================
|
11
|
+
|
12
|
+
Happy Coding.
|
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= minglr
|
2
2
|
|
3
|
-
* http://github.com/
|
3
|
+
* http://github.com/schubert/minglr/tree/master
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
@@ -71,7 +71,7 @@ Minglr can be used regardless your application is a Ruby on Rails web applicatio
|
|
71
71
|
|
72
72
|
== INSTALL:
|
73
73
|
|
74
|
-
* sudo gem install
|
74
|
+
* sudo gem install schubert-minglr
|
75
75
|
* Preferably install rb-password (http://www.caliban.org/ruby/ruby-password.shtml), through Macports (sudo port install rb-password)
|
76
76
|
* Enable Mingle API basic authentication in auth_config.yml, then restart it.
|
77
77
|
* Copy over the "path-to-minglr/tasks/*.sample.rake" files and start customizing for your check-in needs.
|
@@ -80,6 +80,7 @@ Minglr can be used regardless your application is a Ruby on Rails web applicatio
|
|
80
80
|
|
81
81
|
(The MIT License)
|
82
82
|
|
83
|
+
Copyright (c) 2009 Michael Schubert
|
83
84
|
Copyright (c) 2009 Stephen Chu
|
84
85
|
|
85
86
|
Permission is hereby granted, free of charge, to any person obtaining
|
data/Rakefile
CHANGED
@@ -4,7 +4,7 @@ require File.dirname(__FILE__) + '/lib/minglr'
|
|
4
4
|
# Generate all the Rake tasks
|
5
5
|
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
6
|
$hoe = Hoe.new('minglr', Minglr::VERSION) do |p|
|
7
|
-
p.developer('
|
7
|
+
p.developer('Michael Schubert', 'michael@schubert.cx')
|
8
8
|
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
9
|
p.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
10
10
|
p.rubyforge_name = p.name # TODO this is default value
|
data/lib/minglr.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'activesupport'
|
3
|
+
require 'activeresource'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
require File.dirname(__FILE__) + '/minglr/mingle_resource'
|
7
|
+
require File.dirname(__FILE__) + '/minglr/mtx_options_parser'
|
8
|
+
require File.dirname(__FILE__) + '/minglr/minglr_options_parser'
|
9
|
+
require File.dirname(__FILE__) + '/minglr/minglr_config_parser'
|
10
|
+
require File.dirname(__FILE__) + '/minglr/card'
|
11
|
+
require File.dirname(__FILE__) + '/minglr/user'
|
12
|
+
require File.dirname(__FILE__) + '/minglr/property_definition'
|
13
|
+
require File.dirname(__FILE__) + '/minglr/attachment'
|
14
|
+
require File.dirname(__FILE__) + '/minglr/transition_execution'
|
15
|
+
require File.dirname(__FILE__) + '/minglr/input_cache'
|
16
|
+
require File.dirname(__FILE__) + "/version"
|
17
|
+
|
18
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
19
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
20
|
+
|
21
|
+
module Minglr
|
22
|
+
VERSION = MINGLR_VERSION
|
23
|
+
ROOT = File.expand_path File.join(File.dirname(__FILE__), '..')
|
24
|
+
end
|
data/lib/minglr/card.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
class InputCache
|
2
|
+
class << self
|
3
|
+
def put(key, content)
|
4
|
+
File.open(file_pathname(key), File::CREAT | File::WRONLY | File::TRUNC) { |file| file.write content }
|
5
|
+
end
|
6
|
+
|
7
|
+
def get(key)
|
8
|
+
if content = File.read(file_pathname(key))
|
9
|
+
return nil if content.blank?
|
10
|
+
content
|
11
|
+
end
|
12
|
+
rescue
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def file_pathname(key)
|
19
|
+
File.join(Dir::tmpdir, "#{key.to_s.gsub(/[^\w]/, '')}.entry")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
class MinglrAction
|
2
|
+
|
3
|
+
ACTIONS = ["cards", "card", "create", "update", "move", "users", "attach", "fetch"].sort!
|
4
|
+
|
5
|
+
def self.execute(action, options = [], flag_options = {}, config = {})
|
6
|
+
MinglrAction.new(action.to_sym, options, flag_options, config)
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :action, :options, :flag_options, :config
|
10
|
+
|
11
|
+
def initialize(action, options, flag_options, config)
|
12
|
+
@action = action
|
13
|
+
if action == options[0].to_sym
|
14
|
+
options.shift
|
15
|
+
else
|
16
|
+
options.shift; options.shift
|
17
|
+
end
|
18
|
+
@options = options
|
19
|
+
@flag_options = flag_options
|
20
|
+
@config = config
|
21
|
+
begin
|
22
|
+
self.send(action)
|
23
|
+
rescue ActiveResource::ResourceNotFound => error
|
24
|
+
puts error.message + " for URL #{MingleResource.site}..."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def cards
|
29
|
+
attributes = [:number, :card_type_name, @config[:status_property].to_sym, :name]
|
30
|
+
cards = Card.find(:all)
|
31
|
+
cards = filter_collection(cards, attributes, @options)
|
32
|
+
print_collection(cards, attributes)
|
33
|
+
end
|
34
|
+
|
35
|
+
def users
|
36
|
+
attributes = [:login, :name, :email]
|
37
|
+
users = User.find(:all).collect! { |user| user.user }
|
38
|
+
users = filter_collection(users, attributes, @options)
|
39
|
+
print_collection(users, attributes, "right")
|
40
|
+
end
|
41
|
+
|
42
|
+
def card
|
43
|
+
card_number = @options.first
|
44
|
+
attributes = [:number, :card_type_name, @config[:status_property].to_sym, :name, :description]
|
45
|
+
card = card_by_number(card_number)
|
46
|
+
attachments = Attachment.find(:all, :params => { :card_number => card_number })
|
47
|
+
attachments = attachments.collect do |attachment|
|
48
|
+
"* #{attachment.file_name}: #{MingleResource.site + attachment.url}"
|
49
|
+
end
|
50
|
+
output = <<-EOS
|
51
|
+
Number: #{card.number}
|
52
|
+
Name: #{card.name}
|
53
|
+
Type: #{card.card_type_name}
|
54
|
+
Status: #{card.send(@config[:status_property].to_sym)}
|
55
|
+
Description: #{card.description}
|
56
|
+
|
57
|
+
Attachments:
|
58
|
+
#{attachments.join("\n")}
|
59
|
+
EOS
|
60
|
+
puts output
|
61
|
+
end
|
62
|
+
|
63
|
+
def attach
|
64
|
+
card_number = @options.first
|
65
|
+
if card_to_update = card_by_number(card_number)
|
66
|
+
url = Attachment.site
|
67
|
+
url = (url.to_s.gsub(/#{url.path}$/, '')) + Attachment.collection_path(:card_number => card_number)
|
68
|
+
file_name = @flag_options[:file_attachment]
|
69
|
+
require 'httpclient'
|
70
|
+
if File.exist?(file_name)
|
71
|
+
File.open(file_name) do |file|
|
72
|
+
body = { 'file' => file, "filename" => file_name }
|
73
|
+
client = HTTPClient.new
|
74
|
+
client.set_auth(nil, @config[:username], @config[:password])
|
75
|
+
res = client.post(url, body)
|
76
|
+
if res.status_code == 201
|
77
|
+
puts "File '#{file_name}' attached to card #{card_number}"
|
78
|
+
else
|
79
|
+
puts "Error attaching file '#{file_name}' to card #{card_number} (Got back HTTP code #{res.status_code})"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
else
|
83
|
+
puts "Unable to open file '#{file_name}'"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def fetch
|
89
|
+
card_number = @options.first
|
90
|
+
if card_to_update = card_by_number(card_number)
|
91
|
+
attachments = Attachment.find(:all, :params => { :card_number => card_number })
|
92
|
+
attachments.each do |attachment|
|
93
|
+
url = MingleResource.site + attachment.url
|
94
|
+
url.userinfo = nil, nil
|
95
|
+
puts "Downloading #{url.to_s}:"
|
96
|
+
`curl --insecure --progress-bar --output #{attachment.file_name} --user #{@config[:username]}:#{@config[:password]} #{url}`
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def create
|
102
|
+
@flag_options.merge!({ @config[:status_property].to_sym => "New", :cp_owner_user_id => owner_id })
|
103
|
+
card = Card.new(@flag_options)
|
104
|
+
card.save
|
105
|
+
card.reload
|
106
|
+
puts "Card #{card.number} created"
|
107
|
+
end
|
108
|
+
|
109
|
+
def update
|
110
|
+
@flag_options.merge!({ :cp_owner_user_id => owner_id })
|
111
|
+
card_number = @options.first
|
112
|
+
card_to_update = card_by_number(card_number)
|
113
|
+
@flag_options.each do |attribute, value|
|
114
|
+
card_to_update.send("#{attribute.to_s}=".to_sym, value)
|
115
|
+
end
|
116
|
+
card_to_update.save
|
117
|
+
puts "Card #{card_to_update.number} updated\n\n"
|
118
|
+
card
|
119
|
+
end
|
120
|
+
|
121
|
+
def move
|
122
|
+
card_number = @options.first
|
123
|
+
card_to_move = card_by_number(card_number)
|
124
|
+
transition_options = { :card => card_number }
|
125
|
+
transition_options.merge!({ :comment => @flag_options[:comment]}) if @flag_options[:comment]
|
126
|
+
current_status = card_to_move.send(@config[:status_property])
|
127
|
+
next_transition = nil
|
128
|
+
|
129
|
+
case card_to_move.card_type_name.downcase
|
130
|
+
when /task/
|
131
|
+
status_states = @config.select do |key, value|
|
132
|
+
key.to_s =~ /^task_state_/
|
133
|
+
end
|
134
|
+
when /story/
|
135
|
+
status_states = @config.select do |key, value|
|
136
|
+
key.to_s =~ /^story_state/
|
137
|
+
end
|
138
|
+
else
|
139
|
+
puts "No transitions defined for card of type #{card_to_move.card_type_name}"
|
140
|
+
end
|
141
|
+
status_states = status_states.collect {|state| state.last }.collect {|state| state.split(">").collect { |value| value.strip } }
|
142
|
+
next_transition = status_states.select {|state| state.first.downcase == current_status.downcase }.first.last
|
143
|
+
transition_options.merge!({ :transition => next_transition })
|
144
|
+
|
145
|
+
if response = TransitionExecution.create(transition_options)
|
146
|
+
if response.attributes["status"] == "completed"
|
147
|
+
puts "Moved card from #{current_status} to #{next_transition}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def pickup
|
153
|
+
raise "not implemented yet"
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def filter_collection(collection, attributes, words)
|
159
|
+
words.each do |word|
|
160
|
+
collection = collection.select do |element|
|
161
|
+
output = ""
|
162
|
+
attributes.each { |attribute| output << element.send(attribute).to_s + " " }
|
163
|
+
output =~ /#{word}/i
|
164
|
+
end
|
165
|
+
end
|
166
|
+
collection
|
167
|
+
end
|
168
|
+
|
169
|
+
def print_collection(collection, attributes, align = "left")
|
170
|
+
output = []
|
171
|
+
longest_attributes = Array.new(attributes.length, 0)
|
172
|
+
alignment = (align == "left" ? :ljust : :rjust)
|
173
|
+
collection.each do |element|
|
174
|
+
entry = []
|
175
|
+
attributes.each_with_index do |attribute, index|
|
176
|
+
attribute_value = element.send(attribute).to_s
|
177
|
+
longest_attributes[index] = attribute_value.length if attribute_value.length > longest_attributes[index]
|
178
|
+
entry << attribute_value
|
179
|
+
end
|
180
|
+
output << entry
|
181
|
+
end
|
182
|
+
output.each do |entry|
|
183
|
+
row = []
|
184
|
+
entry.each_with_index do |part, index|
|
185
|
+
row << [part.send(alignment, longest_attributes[index])]
|
186
|
+
end
|
187
|
+
puts row.join(" - ")
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def card_by_number(number)
|
192
|
+
Card.find(number)
|
193
|
+
end
|
194
|
+
|
195
|
+
def owner_id
|
196
|
+
user = User.find(:all).select { |user| user.user.login == @config[:username] }.first
|
197
|
+
user.user_id
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class MinglrConfigParser
|
2
|
+
CONFIG_FILE = ".minglrconfig"
|
3
|
+
attr_reader :config
|
4
|
+
|
5
|
+
def self.parse
|
6
|
+
config_files = [File.join(ENV["HOME"], CONFIG_FILE), File.join(ENV["PWD"], CONFIG_FILE)]
|
7
|
+
config_files.each do |config_file_name|
|
8
|
+
if File.exist?(config_file_name)
|
9
|
+
return self.new(File.read(config_file_name)).config
|
10
|
+
end
|
11
|
+
end
|
12
|
+
puts "Unable to find #{CONFIG_FILE} in #{config_files.join(", ")}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(config_contents)
|
16
|
+
@config = {}
|
17
|
+
@current_section = nil
|
18
|
+
config_contents.each_line do |line|
|
19
|
+
line = line.strip!
|
20
|
+
case line
|
21
|
+
when ""
|
22
|
+
next
|
23
|
+
when /\[(.*)\]/
|
24
|
+
define_section($1.to_s)
|
25
|
+
else
|
26
|
+
define_var(line)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
@config
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_section(section_name)
|
33
|
+
@config[section_name.to_sym] = {} unless @config.has_key?(section_name.to_sym)
|
34
|
+
@current_section = section_name.to_sym
|
35
|
+
end
|
36
|
+
|
37
|
+
def define_var(line)
|
38
|
+
key, value = line.split("=")
|
39
|
+
key.strip!
|
40
|
+
value.strip!
|
41
|
+
@config[@current_section][key.to_sym] = value
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class MinglrOptionsParser
|
2
|
+
def self.parse(args, *required_by_command)
|
3
|
+
project_options = []
|
4
|
+
|
5
|
+
if MingleResource.site
|
6
|
+
properties = PropertyDefinition.find(:all)
|
7
|
+
project_options = properties.collect do |property|
|
8
|
+
[property.column_name.to_sym, property.name]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
command_options = {}
|
12
|
+
parser = OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: minglr [action] [options]"
|
14
|
+
opts.separator ""
|
15
|
+
opts.separator "Valid Commands Are: #{MinglrAction::ACTIONS.join(", ")}"
|
16
|
+
|
17
|
+
opts.on("-n NAME", String, "Short name of card") do |card_name|
|
18
|
+
command_options[:name] = card_name
|
19
|
+
end
|
20
|
+
|
21
|
+
opts.on("-d DESCRIPTION", String, "Description of card") do |card_description|
|
22
|
+
command_options[:description] = card_description
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("-t TYPE", String, "Type of card") do |card_type|
|
26
|
+
command_options[:card_type_name] = card_type
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("-c COMMENT", String, "Comment") do |comment|
|
30
|
+
command_options[:comment] = comment
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("-f FILE", String, "File to attach") do |file|
|
34
|
+
command_options[:file_attachment] = file
|
35
|
+
end
|
36
|
+
|
37
|
+
unless project_options.empty?
|
38
|
+
opts.separator ""
|
39
|
+
opts.separator "Project Specific Options"
|
40
|
+
|
41
|
+
project_options.each do |option_pair|
|
42
|
+
option_switch = option_pair[1].downcase.gsub(/\s|_/, "-").gsub(/--|---/, '-').gsub(/--/, '-')
|
43
|
+
opts.on("--#{option_switch}", String, "Set the #{option_pair[1]} for a card") do |option|
|
44
|
+
command_options[option_pair[0]] = option
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.separator ""
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on_tail("-h", "--help", "Show this help message.") do |help|
|
52
|
+
puts opts
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on_tail("--version", "Show version") do
|
57
|
+
puts Minglr::VERSION
|
58
|
+
exit
|
59
|
+
end
|
60
|
+
|
61
|
+
if args.empty?
|
62
|
+
puts opts
|
63
|
+
exit
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
parser.parse! args
|
68
|
+
command_options
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class MtxOptionsParser
|
2
|
+
def self.parse(args, *required_by_command)
|
3
|
+
uri_options = {}
|
4
|
+
command_options = {}
|
5
|
+
|
6
|
+
parser = OptionParser.new do |opts|
|
7
|
+
opts.banner = "Usage: mtx [options]"
|
8
|
+
opts.on("--transition TRANSITION", "Transition name.") do |transition|
|
9
|
+
command_options[:transition] = transition
|
10
|
+
end
|
11
|
+
|
12
|
+
opts.on("--card CARD", "Card number.") do |card|
|
13
|
+
command_options[:card] = card
|
14
|
+
end
|
15
|
+
|
16
|
+
opts.on("--properties ARGS", Array, "User-entered properties and values for the transition in array format. Must be an even number of comma-delimited values, like \"A,B,'C with spaces','D with spaces'\".") do |args|
|
17
|
+
command_options[:properties] = args.in_groups_of(2).map { |key, value| {'name' => key, 'value' => value} }
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on("--comment COMMENT", "Transition comment. This may be required depending on your transition settings.") do |comment|
|
21
|
+
command_options[:comment] = comment
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("--username USERNAME", "Mingle username.") do |username|
|
25
|
+
uri_options[:username] = username
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("--password PASSWORD", "Mingle password.") do |password|
|
29
|
+
uri_options[:password] = password
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on("--host_port HOST_PORT", "Host and port.") do |host_and_port|
|
33
|
+
uri_options[:host_and_port] = host_and_port
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on("--project PROJECT", "Project name.") do |project|
|
37
|
+
uri_options[:project] = project
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
parser.parse! args
|
42
|
+
|
43
|
+
([:project, :host_and_port] | required_by_command).each do |arg|
|
44
|
+
unless command_options[arg] || uri_options[arg]
|
45
|
+
# TODO: let commands handle their own errors
|
46
|
+
$stderr.puts "Missing command-line argument --#{arg.to_s}, use --help for command-line options."
|
47
|
+
exit 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
[uri_options, command_options]
|
52
|
+
end
|
53
|
+
end
|
data/lib/minglr/user.rb
ADDED
data/lib/version.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
MINGLR_VERSION = "1.0.6"
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'minglr'
|
2
|
+
|
3
|
+
desc "Run this if you have new code to check in"
|
4
|
+
task :check_in => %w( minglr:dev_complete:prepare
|
5
|
+
svn:add
|
6
|
+
svn:rm
|
7
|
+
svn:up
|
8
|
+
svn:conflict_check
|
9
|
+
db:migrate
|
10
|
+
test
|
11
|
+
svn:ci
|
12
|
+
minglr:dev_complete:transition )
|
13
|
+
|
14
|
+
namespace :minglr do
|
15
|
+
task :prepare do
|
16
|
+
@host_port = 'mingle-host:8080' # REPLACE WITH YOUR MINGLE HOST AND PORT
|
17
|
+
@project = 'project_4808' # REPLACE WITH YOUR MINGLE PROJECT IDENTIFIER
|
18
|
+
end
|
19
|
+
|
20
|
+
namespace :dev_complete do
|
21
|
+
desc "Gather inputs to execute a 'Development Complete' card transition."
|
22
|
+
task :prepare => 'minglr:prepare' do
|
23
|
+
@transition = 'Transition Uno' # REPLACE WITH THE TRANSITION YOU WANT TO EXECUTE
|
24
|
+
@username = prompt 'Please enter your Mingle username', :cache_key => 'username'
|
25
|
+
@password = prompt 'Please enter your Mingle password', :secure => true
|
26
|
+
@card = prompt 'Please enter a Mingle card number', :cache_key => 'card'
|
27
|
+
@commit_message = prompt 'Please enter a commit message', :cache_key => 'commit_message'
|
28
|
+
@comment = prompt 'Please enter a comment for the transition', :cache_key => 'transition_comment'
|
29
|
+
@properties = ['Actual Effort', 'Property Dos'].inject([]) do |memo, property_name|
|
30
|
+
memo << [ %Q|"#{escape(property_name)}"|,
|
31
|
+
%Q|"#{prompt(%Q{Please enter the value for the property "#{property_name}"}, :cache_key => property_name)}"|]
|
32
|
+
memo
|
33
|
+
end.flatten.join(',')
|
34
|
+
@story_complete = prompt('Is the story complete? (y/N)', :cache_key => 'story_complete').to_s.downcase == 'y'
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Executes a 'Development Complete' card transition."
|
38
|
+
task :transition => 'minglr:dev_complete:prepare' do
|
39
|
+
if @story_complete
|
40
|
+
cmd = %Q|#{Minglr::ROOT}/bin/mtx --username "#{@username}" --password "#{@password}" --card "#{@card}" --comment "#{@comment}" --transition "#{@transition}" --properties #{@properties} --host_port "#{@host_port}" --project "#{@project}"|
|
41
|
+
`#{cmd}`
|
42
|
+
raise "Minglr transition failed with exit status #{$?}." unless $?.success?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def prompt(message, options = { :secure => false })
|
50
|
+
require 'rubygems'
|
51
|
+
begin
|
52
|
+
require 'password' rescue Exception
|
53
|
+
rescue Exception
|
54
|
+
@clear_text_password = true
|
55
|
+
end
|
56
|
+
|
57
|
+
user_input = if options[:secure]
|
58
|
+
if @clear_text_password
|
59
|
+
puts "NOTE: You do not have the 'rb-password' port installed. Perform 'sudo port install rb-password' to avoid your password shown in clear text as you type below."
|
60
|
+
print "#{message}: "
|
61
|
+
$stdin.gets.chop
|
62
|
+
else
|
63
|
+
Password.get "#{message}: "
|
64
|
+
end
|
65
|
+
else
|
66
|
+
print "#{message}"
|
67
|
+
if cache = InputCache.get(options[:cache_key])
|
68
|
+
print " [#{cache}]"
|
69
|
+
end
|
70
|
+
print ": "
|
71
|
+
$stdin.gets.chop
|
72
|
+
end
|
73
|
+
|
74
|
+
if !options[:secure] && cache_key = options.delete(:cache_key)
|
75
|
+
user_input = InputCache.get(cache_key) if user_input.blank?
|
76
|
+
InputCache.put(cache_key, user_input)
|
77
|
+
end
|
78
|
+
|
79
|
+
user_input = escape(user_input)
|
80
|
+
end
|
81
|
+
|
82
|
+
def escape(user_input)
|
83
|
+
user_input.gsub(/["]/, %q{\"}) if user_input
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
namespace :svn do
|
2
|
+
desc "Updates your local WC"
|
3
|
+
task :up do
|
4
|
+
`svn up`
|
5
|
+
end
|
6
|
+
|
7
|
+
desc "Adds new local WC files"
|
8
|
+
task :add do
|
9
|
+
`svn st | grep '^?' | xargs svn add`
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Removes deleted local WC files"
|
13
|
+
task :rm do
|
14
|
+
`svn st | grep '^!' | xargs svn rm`
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Check-in your local WC changes"
|
18
|
+
task :ci do
|
19
|
+
`svn ci -m "#{@commit_message || ENV['commit_message']}"`
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Checks if your WC has merge conflicts."
|
23
|
+
task :conflict_check do
|
24
|
+
`svn st | awk '{ if( $1 == "C" ) exit 1}'`
|
25
|
+
raise 'You have conflicts!' unless $?.success?
|
26
|
+
end
|
27
|
+
end
|
metadata
CHANGED
@@ -1,39 +1,99 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: schubert-minglr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Stephen Chu
|
8
|
-
- Chris O'Meara
|
9
7
|
- Michael Schubert
|
10
8
|
autorequire:
|
11
9
|
bindir: bin
|
12
10
|
cert_chain: []
|
13
11
|
|
14
|
-
date: 2009-06-
|
12
|
+
date: 2009-06-02 00:00:00 -07:00
|
15
13
|
default_executable:
|
16
|
-
dependencies:
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.1.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activeresource
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.1.0
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: newgem
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.4.1
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: hoe
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.8.0
|
54
|
+
version:
|
55
|
+
description: "* This gem provides two executable binaries to interact with Mingle (http://mingle.thoughtworks.com/mingle-agile-project-management) through its API. It also has sample interactive Rake task on how to facilitate easy card movements when a card enters/exits the development queue. * mtx is a binary that facilities transition changes for use on rake tasks * minglr is a more interactive tool that provides a quick interface for many common uses"
|
56
|
+
email:
|
57
|
+
- michael@schubert.cx
|
20
58
|
executables:
|
21
|
-
- minglr
|
22
59
|
- mtx
|
60
|
+
- minglr
|
23
61
|
extensions: []
|
24
62
|
|
25
|
-
extra_rdoc_files:
|
26
|
-
|
63
|
+
extra_rdoc_files:
|
64
|
+
- History.txt
|
65
|
+
- Manifest.txt
|
66
|
+
- PostInstall.txt
|
67
|
+
- README.rdoc
|
27
68
|
files:
|
28
|
-
-
|
29
|
-
-
|
69
|
+
- History.txt
|
70
|
+
- Manifest.txt
|
71
|
+
- PostInstall.txt
|
30
72
|
- README.rdoc
|
31
|
-
-
|
32
|
-
|
73
|
+
- Rakefile
|
74
|
+
- bin/mtx
|
75
|
+
- bin/minglr
|
76
|
+
- lib/minglr.rb
|
77
|
+
- lib/version.rb
|
78
|
+
- lib/minglr/attachment.rb
|
79
|
+
- lib/minglr/card.rb
|
80
|
+
- lib/minglr/input_cache.rb
|
81
|
+
- lib/minglr/mingle_resource.rb
|
82
|
+
- lib/minglr/minglr_action.rb
|
83
|
+
- lib/minglr/minglr_config_parser.rb
|
84
|
+
- lib/minglr/minglr_options_parser.rb
|
85
|
+
- lib/minglr/mtx_options_parser.rb
|
86
|
+
- lib/minglr/property_definition.rb
|
87
|
+
- lib/minglr/transition_execution.rb
|
88
|
+
- lib/minglr/user.rb
|
89
|
+
- tasks/commit.sample.rake
|
90
|
+
- tasks/svn.sample.rake
|
91
|
+
has_rdoc: true
|
33
92
|
homepage: http://github.com/schubert/minglr/tree/master
|
34
|
-
post_install_message:
|
35
|
-
rdoc_options:
|
36
|
-
|
93
|
+
post_install_message: PostInstall.txt
|
94
|
+
rdoc_options:
|
95
|
+
- --main
|
96
|
+
- README.rdoc
|
37
97
|
require_paths:
|
38
98
|
- lib
|
39
99
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -50,10 +110,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
110
|
version:
|
51
111
|
requirements: []
|
52
112
|
|
53
|
-
rubyforge_project:
|
113
|
+
rubyforge_project: minglr
|
54
114
|
rubygems_version: 1.2.0
|
55
115
|
signing_key:
|
56
116
|
specification_version: 2
|
57
|
-
summary: Mingle
|
117
|
+
summary: "* This gem provides two executable binaries to interact with Mingle (http://mingle.thoughtworks.com/mingle-agile-project-management) through its API"
|
58
118
|
test_files: []
|
59
119
|
|
data/minglr.gemspec
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
Gem::Specification.new do |s|
|
2
|
-
s.name = "minglr"
|
3
|
-
s.version = "0.1.0.3"
|
4
|
-
s.date = "2009-06-01"
|
5
|
-
s.bindir = 'bin'
|
6
|
-
s.executables << 'minglr'
|
7
|
-
s.executables << 'mtx'
|
8
|
-
s.summary = "Mingle command line tool"
|
9
|
-
s.email = "michael@schubert.cx"
|
10
|
-
s.homepage = "http://github.com/schubert/minglr/tree/master"
|
11
|
-
s.description = "Mingle command line tool"
|
12
|
-
s.authors = ["Stephen Chu", "Chris O'Meara", "Michael Schubert"]
|
13
|
-
s.files = Dir["bin/*"] + Dir["*.txt"] + Dir["lib/minglr/*.rb"] + Dir["lib/*.rb"] + ["minglr.gemspec", "Rakefile", "README.rdoc", "minglrconfig.sample"] + Dir["tasks/*"]
|
14
|
-
end
|
data/minglrconfig.sample
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
[global]
|
2
|
-
default = someproject
|
3
|
-
username = yourusername
|
4
|
-
|
5
|
-
[someproject]
|
6
|
-
url = http://your.mingle.host.com/mingle/projects/someproject
|
7
|
-
username = yourusername
|
8
|
-
password = YOURMINGLEPASSWORD
|
9
|
-
delete_property = cp_story_status
|
10
|
-
delete_state = Deleted
|
11
|
-
status_property = cp_story_status
|
12
|
-
story_state_1 = Ready For Development > Sign Up for Story
|
13
|
-
story_state_2 = Dev In Progress > Dev Complete
|
14
|
-
task_state_1 = New > New to ready for Dev for Task
|
15
|
-
task_state_2 = Ready for Development > Dev in Progress
|
16
|
-
task_state_3 = Dev In Progress > Story from Dev in Progress to Dev Complete
|
17
|
-
story_checkout = story_state_1
|
18
|
-
task_checkout = story_state_2
|