duckworth 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/config/email.yml +15 -0
- data/lib/duckworth.rb +161 -0
- data/lib/toolbag.rb +162 -0
- data/spec/duckworth_spec.rb +162 -0
- data/spec/fixtures/jobs/assign_pending_tasks/builds/9/log +11 -0
- data/spec/fixtures/jobs/assign_pending_tasks/nextBuildNumber +1 -0
- data/spec/fixtures/jobs/assign_pending_tasks/workspace/spec/git_manager_spec.rb +174 -0
- data/spec/fixtures/jobs/build_in_progress/builds/3/log +11 -0
- data/spec/fixtures/jobs/build_in_progress/builds/4/log +8 -0
- data/spec/fixtures/jobs/build_in_progress/nextBuildNumber +1 -0
- data/spec/fixtures/jobs/build_missing_job_directory/builds/1 +0 -0
- metadata +82 -0
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 @@
|
|
1
|
+
10
|
@@ -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 @@
|
|
1
|
+
5
|
File without changes
|
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
|