schubert-minglr 0.1.0.3 → 1.0.6
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/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
|