duckworth 0.0.1

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/config/email.yml ADDED
@@ -0,0 +1,15 @@
1
+ # example:
2
+ # authentication:
3
+ # from: you@yourdomain.com
4
+ # password: your_password_here
5
+ # users:
6
+ # "User 1 Full Name as found in git blame":"associated_email1@domain.com@"
7
+ # "User 2 Full Name as found in git blame":"associated_email2@domain.com@"
8
+ authentication:
9
+ from: 'you@domain.com'
10
+ password: 'password'
11
+
12
+ users:
13
+ 'Joe Schmoe': 'joeschmoe@domain.com'
14
+ 'Jane Doe': 'janedoe@domain.com'
15
+ 'John Jimenez': 'fake-email0@domain.com'
data/lib/duckworth.rb ADDED
@@ -0,0 +1,161 @@
1
+ require 'net/smtp'
2
+ require 'yaml'
3
+ require 'lib/toolbag'
4
+
5
+ class Duckworth
6
+ include Toolbag::Parses
7
+ include Toolbag::Searches
8
+ include Toolbag::Organizes
9
+ include Toolbag::Writes
10
+ include Toolbag::Communicates
11
+
12
+ attr_accessor :build_number, :tasks
13
+
14
+ def initialize( guidlines = {} )
15
+ # call prepare
16
+ prepare( guidlines )
17
+ find and organize
18
+ end
19
+
20
+ def prepare guidlines
21
+ verify_provided_guidlines( guidlines )
22
+ end
23
+
24
+ def find
25
+ # Find the pending specs
26
+ find_pending_specs
27
+ end
28
+
29
+ def organize
30
+ # Organize the pending specs
31
+ find_contributors
32
+ end
33
+
34
+ def mail
35
+ # Mail the pending specs
36
+ end
37
+
38
+ # protected
39
+
40
+ def workspace
41
+ "#{@job_root}/workspace"
42
+ end
43
+
44
+ def log
45
+ "#{@builds}/#{@build_number}/log"
46
+ end
47
+
48
+ private
49
+
50
+ def verify_provided_guidlines guidlines
51
+ root = guidlines[:jenkins_root]
52
+ name = guidlines[:job_name]
53
+ @builds = "#{root}/jobs/#{name}/builds"
54
+ @job_root = "#{root}/jobs/#{name}"
55
+
56
+ raise errors( :no_jenkins_root ) if root.nil?
57
+ unless File.exist?(root) and File.directory?( root )
58
+ raise errors( :dne_jenkins_root )
59
+ end
60
+
61
+ raise errors( :no_job_name ) if name.nil?
62
+ unless directory? "#{root}/jobs/#{name}"
63
+ raise errors( :dne_job_name )
64
+ end
65
+
66
+ next_build = "#{root}/jobs/#{name}/nextBuildNumber"
67
+
68
+ unless file? next_build
69
+ raise errors( :no_build_number )
70
+ end
71
+
72
+ unless directory? "#{@builds}/#{last_build_number( next_build )}"
73
+ raise errors(:dne_build_number)
74
+ end
75
+ end
76
+
77
+ def errors why_i_refuse_to_work
78
+ general_help = "" +
79
+ "Create new instances like Duckworth.new( " +
80
+ ":jenkins_root => <directory_containing_jobs_directory>, " +
81
+ ":job_name => <name_of_job_duckworth_will_work_on>)"
82
+
83
+ { :no_jenkins_root => "Duckworth cannot work without a :jenkins_root. ",
84
+ :no_job_name => "Duckworth cannot work without a :job_name. ",
85
+ :dne_jenkins_root => "Duckworth cannot find path for :jenkins_root.",
86
+ :dne_job_name => "Duckworth cannot find path for :job_name.",
87
+ :no_build_number => "Duckworth cannot find nextBuildNumber file.",
88
+ :dne_build_number => "Duckworth cannot find log file for nextBuildNumber."
89
+ }[why_i_refuse_to_work] +
90
+ ( why_i_refuse_to_work.to_s.match( /dne|build_number/ ) ? '' : general_help )
91
+ end
92
+
93
+ def file? file
94
+ File.exist?( file ) and File.file?( file )
95
+ end
96
+
97
+ def directory? directory
98
+ File.exist?( directory ) and File.directory?( directory )
99
+ end
100
+
101
+ def last_build_number( next_build_number_file )
102
+ build_number = File.open(
103
+ next_build_number_file, 'r'
104
+ ).readlines.each { |line| line }.first.to_i - 1
105
+
106
+ unless File.exist?( "#{@builds}/#{build_number}/log" )
107
+ raise errors(:dne_build_number)
108
+ end
109
+
110
+ job_in_progress = File.open(
111
+ "#{@builds}/#{build_number}/log", 'r'
112
+ ).readlines.select { |line| line.match /Finished: (SUCCESS|FAILURE)/ }.empty?
113
+
114
+ build_number -= 1 if job_in_progress
115
+ raise 'First Build Still in progress' if build_number == 0
116
+ @build_number = build_number
117
+ build_number
118
+ end
119
+
120
+ def found_pending_marker line
121
+ line.match( /^[\s\t]*Pending:/ )
122
+ end
123
+
124
+ def found_new_spec_marker line, status
125
+ ( status == 'start' || status == 'updating spec data' ) && !line.match( /^[\s\t]*#/ )
126
+ end
127
+
128
+ def found_spec_details_marker line, status
129
+ ( status == 'found new pending spec' || status == 'updating spec data' ) && line.match( /^[\s\t]*\#/ )
130
+ end
131
+
132
+ def find_pending_specs
133
+ logfile = File.new( log )
134
+ status = ''
135
+ @tasks = {}
136
+ task = {}
137
+
138
+ logfile.readlines.each do |line|
139
+
140
+ if found_pending_marker line
141
+ status = 'start'
142
+
143
+ elsif found_new_spec_marker line, status
144
+ status = 'found new pending spec'
145
+ task = {}
146
+ task[:name] = line.chomp
147
+ task[:details] = []
148
+
149
+ elsif found_spec_details_marker line, status
150
+ status = 'updating spec data'
151
+ if line.match /(spec.*)[:](\d+)/
152
+ task[:spec_file] = $1
153
+ task[:line_number] = $2
154
+ else
155
+ task[:details].push line.chomp
156
+ end
157
+ @tasks[task[:name]] = task
158
+ end
159
+ end
160
+ end
161
+ end
data/lib/toolbag.rb ADDED
@@ -0,0 +1,162 @@
1
+ module Toolbag
2
+
3
+ module Parses
4
+ def found_pending_marker line
5
+ line.match( /^[\s\t]*Pending:/ )
6
+ end
7
+
8
+ def found_new_spec_marker line, status
9
+ ( status == 'start' || status == 'updating spec data' ) && !line.match( /^[\s\t]*#/ )
10
+ end
11
+
12
+ def found_spec_details_marker line, status
13
+ ( status == 'found new pending spec' || status == 'updating spec data' ) && line.match( /^[\s\t]*\#/ )
14
+ end
15
+
16
+ def find_pending_specs
17
+ logfile = File.new( @log_file_name )
18
+ status = ''
19
+ @tasks = {}
20
+ task = {}
21
+
22
+ logfile.readlines.each do |line|
23
+
24
+ if found_pending_marker line
25
+ status = 'start'
26
+
27
+ elsif found_new_spec_marker line, status
28
+ status = 'found new pending spec'
29
+ task = {}
30
+ task[:name] = line.chomp
31
+ task[:details] = []
32
+
33
+ elsif found_spec_details_marker line, status
34
+ status = 'updating spec data'
35
+ if line.match /(spec.*)[:](\d+)/
36
+ task[:spec_file] = $1
37
+ task[:line_number] = $2
38
+ else
39
+ task[:details].push line.chomp
40
+ end
41
+ @tasks[task[:name]] = task
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ module Searches
48
+ def search
49
+ @tasks.each_pair do |key, attributes|
50
+ contributors = []
51
+ `git blame "#{attributes[:spec_file]}"`.each do |blame|
52
+ contributor = blame.split(")")[0].split("(")[1]
53
+ contributor = contributor.split(/\s+\d/)[0]
54
+ contributors.push( contributor )
55
+ end
56
+ @tasks[key][:contributors] = contributors.uniq
57
+ @tasks[key][:contributors].reject!{ |contributor| contributor == 'Not Committed Yet'}
58
+ end
59
+ @tasks
60
+ end
61
+
62
+ def find_contributors
63
+ Dir.chdir( workspace ) { search }
64
+ @tasks
65
+ end
66
+ end
67
+
68
+ module Organizes
69
+ def tasks_by_contributor
70
+ open_addressbook
71
+ consolidated_emails = {}
72
+ @email_mappings.each_key do |contributor|
73
+ consolidated_emails[contributor] = []
74
+ @tasks.each do |object|
75
+ key = object[0]
76
+ values = object[1]
77
+ if values[:contributors].include?( contributor )
78
+ message = "\n" +
79
+ " Spec: #{values[:spec_file]}:#{values[:line_number]}\n" +
80
+ " Collaborators: #{values[:contributors].join(',')}\n" +
81
+ " Title: #{values[:name]}\n" +
82
+ " Details: #{values[:details].join('\n')}"
83
+ consolidated_emails[contributor].push( message )
84
+ end
85
+ end
86
+ consolidated_emails[contributor] = consolidated_emails[contributor].join(
87
+ "\n\n"
88
+ )
89
+ end
90
+ consolidated_emails
91
+ end
92
+ end
93
+
94
+ module Writes
95
+ def stdout key, attributes
96
+ puts "\nPending Spec Information:"
97
+ puts " Description: #{key}"
98
+ puts " Contributors: #{attributes[:contributors].join(', ')}"
99
+ puts " File: #{attributes[:spec_file]}"
100
+ puts " Line: #{attributes[:line_number]}\n"
101
+ end
102
+ end
103
+
104
+ module Communicates
105
+
106
+ def open_addressbook
107
+ config = YAML::load_file( './config/email.yml' )
108
+ @email_from = config['authentication']['from']
109
+ @email_password = config['authentication']['password']
110
+ @email_mappings = config['users']
111
+ end
112
+
113
+ def blame(options = nil)
114
+ if !options[:spam]
115
+ self.tasks_by_contributor.each_pair do |recipient, message|
116
+ if message == ''
117
+ subject = 'Congratulations, you have no pending specs! :)'
118
+ messy = 'Woot!'
119
+ else
120
+ subject = "Please collaborate to fix consolidated list of specs: "
121
+ messy = message
122
+ end
123
+
124
+ send_gmail(
125
+ :recipients => [recipient],
126
+ :subject => subject,
127
+ :message => messy
128
+ )
129
+ end
130
+ else
131
+ self.tasks.each_pair do |key, attributes|
132
+ if options.nil? || !options[:email]
133
+ stdout key, attributes
134
+ else
135
+ send_gmail :recipients => attributes[:contributors],
136
+ :subject => "Please collaborate to fix spec: " +
137
+ "#{attributes[:spec_file]} : " +
138
+ "#{attributes[:line_number]}",
139
+ :message => "Spec Details:\n " +
140
+ "Details\n #{attributes[:details].join('\n')}\n" +
141
+ "Collaborators:\n #{attributes[:contributors].join(', ')}\n"
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ def send_gmail options
148
+ open_addressbook
149
+ recipients = options[:recipients]
150
+ msg = "Subject: #{options[:subject]}\n\n#{options[:message]}"
151
+ smtp = Net::SMTP.new 'smtp.gmail.com', 587
152
+ smtp.enable_starttls
153
+ puts "Sending e-mail to #{recipients.join(',')}"
154
+ smtp.start( '', @email_from, @email_password, :login ) do
155
+ smtp.send_message( msg, @email_from, recipients.map{ |recipient|
156
+ # @email_mappings["#{recipient.gsub(' ', '_') }" ]
157
+ @email_mappings[ recipient ]
158
+ })
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,162 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+ require 'lib/duckworth'
4
+
5
+ describe Duckworth do
6
+ describe "Preparation" do
7
+ before :each do
8
+ @errors = {
9
+ :no_jenkins_root => "" +
10
+ "Duckworth cannot work without a :jenkins_root. " +
11
+ "Create new instances like Duckworth.new( " +
12
+ ":jenkins_root => <directory_containing_jobs_directory>, " +
13
+ ":job_name => <name_of_job_duckworth_will_work_on>)",
14
+
15
+ :no_job_name => "" +
16
+ "Duckworth cannot work without a :job_name. " +
17
+ "Create new instances like Duckworth.new( " +
18
+ ":jenkins_root => <directory_containing_jobs_directory>, " +
19
+ ":job_name => <name_of_job_duckworth_will_work_on>)",
20
+
21
+ :dne_jenkins_root => "Duckworth cannot find path for :jenkins_root.",
22
+ :dne_job_name => "Duckworth cannot find path for :job_name.",
23
+ :no_build_number => "Duckworth cannot find nextBuildNumber file.",
24
+ :dne_build_number => "Duckworth cannot find log file for nextBuildNumber."
25
+ }
26
+ end
27
+
28
+ it "refuses to work without proper instructions" do
29
+ lambda{ Duckworth.new }.should raise_error @errors[:no_jenkins_root]
30
+ lambda{
31
+ Duckworth.new( :jenkins_root => 'asdf')
32
+ }.should raise_error @errors[:dne_jenkins_root]
33
+
34
+ lambda{
35
+ Duckworth.new(:jenkins_root => '/')
36
+ }.should raise_error @errors[:no_job_name]
37
+
38
+ end
39
+
40
+ it "refuses to work on wild goose chases" do
41
+ lambda{
42
+ Duckworth.new(:jenkins_root => '/', :job_name => 'asdf')
43
+ }.should raise_error @errors[:dne_job_name]
44
+
45
+ lambda{
46
+ Duckworth.new(
47
+ :jenkins_root => './spec/fixtures',
48
+ :job_name => 'malformed_job_directory'
49
+ )
50
+ }.should raise_error @errors[:no_build_number]
51
+
52
+ lambda{
53
+ Duckworth.new(
54
+ :jenkins_root => './spec/fixtures',
55
+ :job_name => 'build_missing_job_directory'
56
+ )
57
+ }.should raise_error @errors[:dne_build_number]
58
+
59
+ end
60
+
61
+ it "knows when build is in progress, and will revert previous build" do
62
+ Duckworth.new(
63
+ :jenkins_root => './spec/fixtures',
64
+ :job_name => 'build_in_progress'
65
+ ).build_number.should == 3
66
+ end
67
+
68
+ end
69
+
70
+ it "prepares for work when initialized with proper instructions" do
71
+ lambda{
72
+ @duckworth = Duckworth.new(
73
+ :jenkins_root => './spec/fixtures',
74
+ :job_name => 'assign_pending_tasks'
75
+ )
76
+ }.should_not raise_error
77
+
78
+ @duckworth.class.should == Duckworth
79
+
80
+ @duckworth.workspace.should ==
81
+ './spec/fixtures/jobs/assign_pending_tasks/workspace'
82
+
83
+ @duckworth.log.should ==
84
+ './spec/fixtures/jobs/assign_pending_tasks/builds/9/log'
85
+
86
+ @duckworth.build_number.should == 9
87
+ end
88
+ end
89
+
90
+ describe Duckworth do
91
+ before :each do
92
+ @test_01 = ' Fake test 1 spec/git_manager_spec.rb'
93
+ @test_02 = ' Fake test 2 spec/git_manager_spec.rb'
94
+
95
+ @duckworth = Duckworth.new(
96
+ :jenkins_root => './spec/fixtures',
97
+ :job_name => 'assign_pending_tasks'
98
+ )
99
+
100
+ @duckworth.blame( :spam => false )
101
+
102
+ @expected_tasks = {
103
+ @test_01 => {
104
+ :spec_file => 'spec/git_manager_spec.rb',
105
+ :details=>[" # Test 123"],
106
+ :line_number => '13',
107
+ :contributors => ['John Jimenez'],
108
+ :name => " Fake test 1 spec/git_manager_spec.rb"
109
+ },
110
+ @test_02 => {
111
+ :spec_file => 'spec/git_manager_spec.rb',
112
+ :details=>[" # Test 123"],
113
+ :line_number => '11',
114
+ :contributors => ['John Jimenez'],
115
+ :name => " Fake test 2 spec/git_manager_spec.rb"
116
+ }
117
+ }
118
+ end
119
+
120
+ it "collects pending specs as expected" do
121
+ @duckworth.tasks[@test_01][:spec_file].should == @expected_tasks[@test_01][:spec_file]
122
+ @duckworth.tasks[@test_02][:spec_file].should == @expected_tasks[@test_02][:spec_file]
123
+ end
124
+
125
+ it "collects pending spec line number as expected" do
126
+ @duckworth.tasks[@test_01][:line_number].should == @expected_tasks[@test_01][:line_number]
127
+ @duckworth.tasks[@test_02][:line_number].should == @expected_tasks[@test_02][:line_number]
128
+ end
129
+
130
+ it "collects pending spec name as expected" do
131
+ @duckworth.tasks[@test_01][:spec_name].should == @expected_tasks[@test_01][:spec_name]
132
+ @duckworth.tasks[@test_02][:spec_name].should == @expected_tasks[@test_02][:spec_name]
133
+ end
134
+
135
+ it "collects pending spec contributors as expected" do
136
+ @duckworth.tasks[@test_01][:contributors].should == @expected_tasks[@test_01][:contributors]
137
+ @duckworth.tasks[@test_02][:contributors].should == @expected_tasks[@test_02][:contributors]
138
+ end
139
+
140
+ it "collects pending spec details as expected" do
141
+ @duckworth.tasks[@test_01][:details].should == @expected_tasks[@test_01][:details]
142
+ @duckworth.tasks[@test_02][:details].should == @expected_tasks[@test_02][:details]
143
+ end
144
+
145
+ it "consolidates blames into structure for e-mailing each developer once" do
146
+ expected_text = "\n" +
147
+ " Spec: spec/git_manager_spec.rb:11\n" +
148
+ " Collaborators: John Jimenez\n" +
149
+ " Title: Fake test 2 spec/git_manager_spec.rb\n" +
150
+ " Details: # Test 123\n\n\n" +
151
+ " Spec: spec/git_manager_spec.rb:13\n" +
152
+ " Collaborators: John Jimenez\n" +
153
+ " Title: Fake test 1 spec/git_manager_spec.rb\n" +
154
+ " Details: # Test 123"
155
+
156
+ @duckworth.tasks_by_contributor.should == {
157
+ "John Jimenez" => expected_text,
158
+ "Joe Schmoe" => '',
159
+ "Jane Doe" => '',
160
+ }
161
+ end
162
+ end
@@ -0,0 +1,11 @@
1
+ Pending:
2
+ Fake test 1 spec/git_manager_spec.rb
3
+ # Test 123
4
+ # ./spec/git_manager_spec.rb:13
5
+ Fake test 2 spec/git_manager_spec.rb
6
+ # Test 123
7
+ # ./spec/git_manager_spec.rb:11
8
+
9
+ Finished in x minutes y seconds
10
+ z examples, 0 failures, 2 pending
11
+ Finished: SUCCESS
@@ -0,0 +1,174 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+ require 'lib/duckworth'
4
+
5
+ describe Duckworth do
6
+ describe "Preparation" do
7
+ before :each do
8
+ @errors = {
9
+ :no_jenkins_root => "" +
10
+ "Duckworth cannot work without a :jenkins_root. " +
11
+ "Create new instances like Duckworth.new( " +
12
+ ":jenkins_root => <directory_containing_jobs_directory>, " +
13
+ ":job_name => <name_of_job_duckworth_will_work_on>)",
14
+
15
+ :no_job_name => "" +
16
+ "Duckworth cannot work without a :job_name. " +
17
+ "Create new instances like Duckworth.new( " +
18
+ ":jenkins_root => <directory_containing_jobs_directory>, " +
19
+ ":job_name => <name_of_job_duckworth_will_work_on>)",
20
+
21
+ :dne_jenkins_root => "Duckworth cannot find path for :jenkins_root.",
22
+ :dne_job_name => "Duckworth cannot find path for :job_name.",
23
+ :no_build_number => "Duckworth cannot find nextBuildNumber file.",
24
+ :dne_build_number => "Duckworth cannot find log file for nextBuildNumber."
25
+ }
26
+ end
27
+
28
+ it "refuses to work without proper instructions" do
29
+ lambda{ Duckworth.new }.should raise_error @errors[:no_jenkins_root]
30
+ lambda{
31
+ Duckworth.new( :jenkins_root => 'asdf')
32
+ }.should raise_error @errors[:dne_jenkins_root]
33
+
34
+ lambda{
35
+ Duckworth.new(:jenkins_root => '/')
36
+ }.should raise_error @errors[:no_job_name]
37
+
38
+ end
39
+
40
+ it "refuses to work on wild goose chases" do
41
+ lambda{
42
+ Duckworth.new(:jenkins_root => '/', :job_name => 'asdf')
43
+ }.should raise_error @errors[:dne_job_name]
44
+
45
+ lambda{
46
+ Duckworth.new(
47
+ :jenkins_root => './spec/fixtures',
48
+ :job_name => 'malformed_job_directory'
49
+ )
50
+ }.should raise_error @errors[:no_build_number]
51
+
52
+ lambda{
53
+ Duckworth.new(
54
+ :jenkins_root => './spec/fixtures',
55
+ :job_name => 'build_missing_job_directory'
56
+ )
57
+ }.should raise_error @errors[:dne_build_number]
58
+
59
+ end
60
+
61
+ it "knows when build is in progress, and will revert previous build" do
62
+ Duckworth.new(
63
+ :jenkins_root => './spec/fixtures',
64
+ :job_name => 'build_in_progress'
65
+ ).build_number.should == 3
66
+ end
67
+
68
+ end
69
+
70
+ it "prepares for work when initialized with proper instructions" do
71
+ lambda{
72
+ @duckworth = Duckworth.new(
73
+ :jenkins_root => './spec/fixtures',
74
+ :job_name => 'assign_pending_tasks'
75
+ )
76
+ }.should_not raise_error
77
+
78
+ @duckworth.class.should == Duckworth
79
+
80
+ @duckworth.workspace.should ==
81
+ './spec/fixtures/jobs/assign_pending_tasks/workspace'
82
+
83
+ @duckworth.log.should ==
84
+ './spec/fixtures/jobs/assign_pending_tasks/builds/9/log'
85
+
86
+ @duckworth.build_number.should == 9
87
+ end
88
+ end
89
+
90
+ describe Duckworth do
91
+ before :each do
92
+ @test_01 = ' Fake test 1 spec/git_manager_spec.rb'
93
+ @test_02 = ' Fake test 2 spec/git_manager_spec.rb'
94
+
95
+ @duckworth = Duckworth.new(
96
+ :jenkins_root => './spec/fixtures',
97
+ :job_name => 'assign_pending_tasks'
98
+ ).blame( :email => false )
99
+
100
+ @expected_tasks = {
101
+ @test_01 => {
102
+ :spec_file => 'spec/git_manager_spec.rb',
103
+ :details=>[" # Test 123"],
104
+ :line_number => '13',
105
+ :contributors => ['John Jimenez'],
106
+ :name => " Fake test 1 spec/git_manager_spec.rb"
107
+ },
108
+ @test_02 => {
109
+ :spec_file => 'spec/git_manager_spec.rb',
110
+ :details=>[" # Test 123"],
111
+ :line_number => '11',
112
+ :contributors => ['John Jimenez'],
113
+ :name => " Fake test 2 spec/git_manager_spec.rb"
114
+ }
115
+ }
116
+ end
117
+
118
+ it "collects pending specs as expected" do
119
+ @duckworth.tasks[@test_01][:spec_file].should == @expected_tasks[@test_01][:spec_file]
120
+ @duckworth.tasks[@test_02][:spec_file].should == @expected_tasks[@test_02][:spec_file]
121
+ end
122
+
123
+ it "collects pending spec line number as expected" do
124
+ @duckworth.tasks[@test_01][:line_number].should == @expected_tasks[@test_01][:line_number]
125
+ @duckworth.tasks[@test_02][:line_number].should == @expected_tasks[@test_02][:line_number]
126
+ end
127
+
128
+ it "collects pending spec name as expected" do
129
+ @duckworth.tasks[@test_01][:spec_name].should == @expected_tasks[@test_01][:spec_name]
130
+ @duckworth.tasks[@test_02][:spec_name].should == @expected_tasks[@test_02][:spec_name]
131
+ end
132
+
133
+ it "collects pending spec contributors as expected" do
134
+ @duckworth.tasks[@test_01][:contributors].should == @expected_tasks[@test_01][:contributors]
135
+ @duckworth.tasks[@test_02][:contributors].should == @expected_tasks[@test_02][:contributors]
136
+ end
137
+
138
+ it "collects pending spec details as expected" do
139
+ @duckworth.tasks[@test_01][:details].should == @expected_tasks[@test_01][:details]
140
+ @duckworth.tasks[@test_02][:details].should == @expected_tasks[@test_02][:details]
141
+ end
142
+
143
+ it "consolidates blames into structure for e-mailing each developer once" do
144
+ expected_text = "\n" +
145
+ " Spec: spec/git_manager_spec.rb:11\n" +
146
+ " Collaborators: John Jimenez\n" +
147
+ " Title: Fake test 2 spec/git_manager_spec.rb\n" +
148
+ " Details: # Test 123\n\n\n" +
149
+ " Spec: spec/git_manager_spec.rb:13\n" +
150
+ " Collaborators: John Jimenez\n" +
151
+ " Title: Fake test 1 spec/git_manager_spec.rb\n" +
152
+ " Details: # Test 123"
153
+
154
+ @duckworth.tasks_by_contributor.should == {
155
+ "John Jimenez" => expected_text,
156
+ "Joe Schmoe" => '',
157
+ "Jane Doe" => '',
158
+ }
159
+ # { " Fake test 2 spec/git_manager_spec.rb"=>{
160
+ # :spec_file=>"spec/git_manager_spec.rb",
161
+ # :details=>[" # Test 123"],
162
+ # :contributors=>["John Jimenez"],
163
+ # :name=>" Fake test 2 spec/git_manager_spec.rb",
164
+ # :line_number=>"11"
165
+ # },
166
+ # " Fake test 1 spec/git_manager_spec.rb"=>{
167
+ # :spec_file=>"spec/git_manager_spec.rb",
168
+ # :details=>[" # Test 123"],
169
+ # :contributors=>["John Jimenez"],
170
+ # :name=>" Fake test 1 spec/git_manager_spec.rb",
171
+ # :line_number=>"13"
172
+ # }
173
+ end
174
+ end
@@ -0,0 +1,11 @@
1
+ Pending:
2
+ Fake test 1 spec/git_manager_spec.rb
3
+ # Test 123
4
+ # ./spec/git_manager_spec.rb:13
5
+ Fake test 2 spec/git_manager_spec.rb
6
+ # Test 123
7
+ # ./spec/git_manager_spec.rb:11
8
+
9
+ Finished in x minutes y seconds
10
+ z examples, 0 failures, 2 pending
11
+ Finished: SUCCESS
@@ -0,0 +1,8 @@
1
+ Pending:
2
+ Fake test 1 spec/git_manager_spec.rb
3
+ # Test 123
4
+ # ./spec/git_manager_spec.rb:13
5
+ Fake test 2 spec/git_manager_spec.rb
6
+ # Test 123
7
+ # ./spec/git_manager_spec.rb:11
8
+
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: duckworth
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - kikuchiyo
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2013-01-12 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: QA Automation Engineer Assistant, for Jenkins and Git.
22
+ email: jimenez.john0@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/toolbag.rb
31
+ - lib/duckworth.rb
32
+ - config/email.yml
33
+ - spec/duckworth_spec.rb
34
+ - spec/fixtures/jobs/assign_pending_tasks/builds/9/log
35
+ - spec/fixtures/jobs/assign_pending_tasks/nextBuildNumber
36
+ - spec/fixtures/jobs/assign_pending_tasks/workspace/spec/git_manager_spec.rb
37
+ - spec/fixtures/jobs/build_in_progress/builds/3/log
38
+ - spec/fixtures/jobs/build_in_progress/builds/4/log
39
+ - spec/fixtures/jobs/build_in_progress/nextBuildNumber
40
+ - spec/fixtures/jobs/build_missing_job_directory/builds/1
41
+ homepage: http://rubygems.org/gems/duckworth
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ hash: 3
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.8.10
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: QA Automation Engineer Assistant
74
+ test_files:
75
+ - spec/duckworth_spec.rb
76
+ - spec/fixtures/jobs/assign_pending_tasks/builds/9/log
77
+ - spec/fixtures/jobs/assign_pending_tasks/nextBuildNumber
78
+ - spec/fixtures/jobs/assign_pending_tasks/workspace/spec/git_manager_spec.rb
79
+ - spec/fixtures/jobs/build_in_progress/builds/3/log
80
+ - spec/fixtures/jobs/build_in_progress/builds/4/log
81
+ - spec/fixtures/jobs/build_in_progress/nextBuildNumber
82
+ - spec/fixtures/jobs/build_missing_job_directory/builds/1