0 | |
#!/usr/bin/env ruby
|
1 | |
|
2 | |
# The tasks associated with building Reductive Labs projects
|
3 | |
|
4 | |
require 'rbconfig'
|
5 | |
require 'rake'
|
6 | |
require 'rake/tasklib'
|
7 | |
|
8 | |
require 'rake/clean'
|
9 | |
require 'rake/testtask'
|
10 | |
|
11 | |
$features = {}
|
12 | |
|
13 | |
begin
|
14 | |
require 'rubygems'
|
15 | |
require 'rake/gempackagetask'
|
16 | |
$features[:gem] = true
|
17 | |
rescue Exception
|
18 | |
$features[:gem] = false
|
19 | |
$stderr.puts "No Gems; skipping"
|
20 | |
nil
|
21 | |
end
|
22 | |
|
23 | |
begin
|
24 | |
require 'rdoc/rdoc'
|
25 | |
$features[:rdoc] = true
|
26 | |
rescue => detail
|
27 | |
$features[:rdoc] = false
|
28 | |
puts "No rdoc: %s" % detail
|
29 | |
end
|
30 | |
|
31 | |
if $features[:rdoc]
|
32 | |
require 'rake/rdoctask'
|
33 | |
end
|
34 | |
|
35 | |
# Create all of the standard targets for a Reductive Labs project.
|
36 | |
# NOTE: The reason so many of the rake tasks are generated, rather than being
|
37 | |
# declared directly, is that they need information from the project instance.
|
38 | |
# Any rake task with an instance variable (e.g., @name or @version) needs
|
39 | |
# to have that variable assigned *before* the task is defined. Suckage.
|
40 | |
class Rake::RedLabProject < Rake::TaskLib
|
41 | |
# The project name.
|
42 | |
attr_accessor :name
|
43 | |
|
44 | |
# The project version.
|
45 | |
attr_accessor :version
|
46 | |
|
47 | |
# The directory to which to publish packages and html and such.
|
48 | |
attr_accessor :publishdir
|
49 | |
|
50 | |
# The package-specific publishing directory
|
51 | |
attr_accessor :pkgpublishdir
|
52 | |
|
53 | |
# Create a Gem file.
|
54 | |
attr_accessor :mkgem
|
55 | |
|
56 | |
# The hosts to run all of our tests on.
|
57 | |
attr_accessor :testhosts
|
58 | |
|
59 | |
# The summary of this project.
|
60 | |
attr_accessor :summary
|
61 | |
|
62 | |
# The description of this project.
|
63 | |
attr_accessor :description
|
64 | |
|
65 | |
# The author of this project.
|
66 | |
attr_accessor :author
|
67 | |
|
68 | |
# A Contact email address.
|
69 | |
attr_accessor :email
|
70 | |
|
71 | |
# The URL for the project.
|
72 | |
attr_accessor :url
|
73 | |
|
74 | |
# Where to get the source code.
|
75 | |
attr_accessor :source
|
76 | |
|
77 | |
# Who the vendor is.
|
78 | |
attr_accessor :vendor
|
79 | |
|
80 | |
# The copyright for this project
|
81 | |
attr_accessor :copyright
|
82 | |
|
83 | |
# The RubyForge project.
|
84 | |
attr_accessor :rfproject
|
85 | |
|
86 | |
# The list of files. Only used for gem tasks.
|
87 | |
attr_writer :filelist
|
88 | |
|
89 | |
# The directory in which to store packages. Defaults to "pkg".
|
90 | |
attr_accessor :package_dir
|
91 | |
|
92 | |
# The default task. Defaults to the 'alltests' task.
|
93 | |
attr_accessor :defaulttask
|
94 | |
|
95 | |
# The defined requirements
|
96 | |
attr_reader :requires
|
97 | |
|
98 | |
# The file containing the version string.
|
99 | |
attr_accessor :versionfile
|
100 | |
|
101 | |
# Print messages on stdout
|
102 | |
def announce(msg = nil)
|
103 | |
puts msg
|
104 | |
end
|
105 | |
|
106 | |
# Print messages on stderr
|
107 | |
def warn(msg = nil)
|
108 | |
$stderr.puts msg
|
109 | |
end
|
110 | |
|
111 | |
def add_dependency(name, version)
|
112 | |
@requires[name] = version
|
113 | |
end
|
114 | |
|
115 | |
# Where we'll be putting the code.
|
116 | |
def codedir
|
117 | |
unless defined? @codedir
|
118 | |
@codedir = File.join(self.package_dir, "#{@name}-#{@version}")
|
119 | |
end
|
120 | |
|
121 | |
return @codedir
|
122 | |
end
|
123 | |
|
124 | |
# Retrieve the current version from the code.
|
125 | |
def currentversion
|
126 | |
unless defined? @currentversion
|
127 | |
ver = %x{ruby -Ilib ./bin/#{@name} --version}.chomp
|
128 | |
if $? == 0 and ver != ""
|
129 | |
@currentversion = ver
|
130 | |
else
|
131 | |
warn "Could not retrieve current version; using 0.0.0"
|
132 | |
@currentversion = "0.0.0"
|
133 | |
end
|
134 | |
end
|
135 | |
|
136 | |
return @currentversion
|
137 | |
end
|
138 | |
|
139 | |
# Define all of our package tasks. We just search through all of our
|
140 | |
# defined methods and call anything that's listed as making tasks.
|
141 | |
def define
|
142 | |
self.methods.find_all { |method| method.to_s =~ /^mktask/ }.each { |method|
|
143 | |
self.send(method)
|
144 | |
}
|
145 | |
end
|
146 | |
|
147 | |
def egrep(pattern)
|
148 | |
Dir['**/*.rb'].each do |fn|
|
149 | |
count = 0
|
150 | |
open(fn) do |f|
|
151 | |
while line = f.gets
|
152 | |
count += 1
|
153 | |
if line =~ pattern
|
154 | |
puts "#{fn}:#{count}:#{line}"
|
155 | |
end
|
156 | |
end
|
157 | |
end
|
158 | |
end
|
159 | |
end
|
160 | |
|
161 | |
# List all of the files.
|
162 | |
def filelist
|
163 | |
unless defined? @createdfilelist
|
164 | |
# If they passed in a file list as an array, then create a FileList
|
165 | |
# object out of it.
|
166 | |
if defined? @filelist
|
167 | |
unless @filelist.is_a? FileList
|
168 | |
@filelist = FileList[@filelist]
|
169 | |
end
|
170 | |
else
|
171 | |
# Use a default file list.
|
172 | |
@filelist = FileList[
|
173 | |
'install.rb',
|
174 | |
'[A-Z]*',
|
175 | |
'lib/**/*.rb',
|
176 | |
'test/**/*.rb',
|
177 | |
'bin/**/*',
|
178 | |
'ext/**/*',
|
179 | |
'examples/**/*',
|
180 | |
'conf/**/*'
|
181 | |
]
|
182 | |
end
|
183 | |
@filelist.delete_if {|item| item.include?(".git")}
|
184 | |
|
185 | |
@createdfilelist = true
|
186 | |
end
|
187 | |
|
188 | |
@filelist
|
189 | |
end
|
190 | |
|
191 | |
def has?(feature)
|
192 | |
feature = feature.intern if feature.is_a? String
|
193 | |
if $features.include?(feature)
|
194 | |
return $features[feature]
|
195 | |
else
|
196 | |
return true
|
197 | |
end
|
198 | |
end
|
199 | |
|
200 | |
def initialize(name, version = nil)
|
201 | |
@name = name
|
202 | |
|
203 | |
if ENV['REL']
|
204 | |
@version = ENV['REL']
|
205 | |
else
|
206 | |
@version = version || self.currentversion
|
207 | |
end
|
208 | |
|
209 | |
@defaulttask = :alltests
|
210 | |
@publishdir = "/opt/rl/docroots/reductivelabs.com/htdocs/downloads"
|
211 | |
@pkgpublishdir = "#{@publishdir}/#{@name}"
|
212 | |
|
213 | |
@email = "dev@reductivelabs.com"
|
214 | |
@url = "http://reductivelabs.com/projects/#{@name}"
|
215 | |
@source = "http://reductivelabs.com/downloads/#{@name}/#{@name}-#{@version}.tgz"
|
216 | |
@vendor = "Reductive Labs, LLC"
|
217 | |
@copyright = "Copyright 2003-2008, Reductive Labs, LLC. Some Rights Reserved."
|
218 | |
@rfproject = @name
|
219 | |
|
220 | |
@defaulttask = :package
|
221 | |
|
222 | |
@package_dir = "pkg"
|
223 | |
|
224 | |
@requires = {}
|
225 | |
|
226 | |
@versionfile = "lib/#{@name}.rb"
|
227 | |
|
228 | |
CLOBBER.include('doc/*')
|
229 | |
|
230 | |
yield self if block_given?
|
231 | |
define if block_given?
|
232 | |
end
|
233 | |
|
234 | |
def mktaskhtml
|
235 | |
if $features[:rdoc]
|
236 | |
Rake::RDocTask.new(:html) { |rdoc|
|
237 | |
rdoc.rdoc_dir = 'html'
|
238 | |
rdoc.template = 'html'
|
239 | |
rdoc.title = @name.capitalize
|
240 | |
rdoc.options << '--line-numbers' << '--inline-source' <<
|
241 | |
'--main' << 'README'
|
242 | |
rdoc.rdoc_files.include('README', 'COPYING', 'CHANGELOG')
|
243 | |
rdoc.rdoc_files.include('lib/**/*.rb')
|
244 | |
CLEAN.include("html")
|
245 | |
}
|
246 | |
|
247 | |
# Publish the html.
|
248 | |
task :publish => [:package, :html] do
|
249 | |
puts Dir.getwd
|
250 | |
sh %{cp -r html #{self.pkgpublishdir}/apidocs}
|
251 | |
end
|
252 | |
else
|
253 | |
warn "No rdoc; skipping html"
|
254 | |
end
|
255 | |
end
|
256 | |
|
257 | |
# Create a release task.
|
258 | |
def mktaskrelease
|
259 | |
desc "Make a new release"
|
260 | |
task :release => [
|
261 | |
:prerelease,
|
262 | |
:clobber,
|
263 | |
:update_version,
|
264 | |
:commit_newversion,
|
265 | |
:trac_version,
|
266 | |
:tag, # tag everything before we make a bunch of extra dirs
|
267 | |
:html,
|
268 | |
:package,
|
269 | |
:publish
|
270 | |
] do
|
271 | |
|
272 | |
announce
|
273 | |
announce "**************************************************************"
|
274 | |
announce "* Release #{@version} Complete."
|
275 | |
announce "* Packages ready to upload."
|
276 | |
announce "**************************************************************"
|
277 | |
announce
|
278 | |
end
|
279 | |
end
|
280 | |
|
281 | |
# Do any prerelease work.
|
282 | |
def mktaskprerelease
|
283 | |
# Validate that everything is ready to go for a release.
|
284 | |
task :prerelease do
|
285 | |
announce
|
286 | |
announce "**************************************************************"
|
287 | |
announce "* Making Release #{@version}"
|
288 | |
announce "* (current version #{self.currentversion})"
|
289 | |
announce "**************************************************************"
|
290 | |
announce
|
291 | |
|
292 | |
# Is a release number supplied?
|
293 | |
unless ENV['REL']
|
294 | |
warn "You must provide a release number when releasing"
|
295 | |
fail "Usage: rake release REL=x.y.z [REUSE=tag_suffix]"
|
296 | |
end
|
297 | |
|
298 | |
# Is the release different than the current release.
|
299 | |
# (or is REUSE set?)
|
300 | |
if @version == self.currentversion && ! ENV['REUSE']
|
301 | |
fail "Current version is #{@version}, must specify REUSE=tag_suffix to reuse version"
|
302 | |
end
|
303 | |
|
304 | |
# Are all source files checked in?
|
305 | |
if ENV['RELTEST']
|
306 | |
announce "Release Task Testing, skipping checked-in file test"
|
307 | |
else
|
308 | |
announce "Checking for unchecked-in files..."
|
309 | |
data = %x{git status}
|
310 | |
unless data.include?("nothing to commit")
|
311 | |
fail "git status is not clean ... do you have unchecked-in files?"
|
312 | |
end
|
313 | |
announce "No outstanding checkins found ... OK"
|
314 | |
end
|
315 | |
end
|
316 | |
end
|
317 | |
|
318 | |
# Create the task to update versions.
|
319 | |
def mktaskupdateversion
|
320 | |
task :update_version => [:prerelease] do
|
321 | |
if @version == self.currentversion
|
322 | |
announce "No version change ... skipping version update"
|
323 | |
else
|
324 | |
announce "Updating #{@versionfile} version to #{@version}"
|
325 | |
open(@versionfile) do |rakein|
|
326 | |
open("#{@versionfile}.new", "w") do |rakeout|
|
327 | |
rakein.each do |line|
|
328 | |
if line =~ /^(\s*)#{@name.upcase}VERSION\s*=\s*/
|
329 | |
rakeout.puts "#{$1}#{@name.upcase}VERSION = '#{@version}'"
|
330 | |
else
|
331 | |
rakeout.puts line
|
332 | |
end
|
333 | |
end
|
334 | |
end
|
335 | |
end
|
336 | |
mv "#{@versionfile}.new", @versionfile
|
337 | |
|
338 | |
end
|
339 | |
end
|
340 | |
|
341 | |
desc "Commit the new versions to SVN."
|
342 | |
task :commit_newversion => [:update_version] do
|
343 | |
if ENV['RELTEST']
|
344 | |
announce "Release Task Testing, skipping commiting of new version"
|
345 | |
else
|
346 | |
sh %{git commit -m "Updated to version #{@version}" #{@versionfile}}
|
347 | |
end
|
348 | |
end
|
349 | |
end
|
350 | |
|
351 | |
def mktasktrac_version
|
352 | |
task :trac_version => [:update_version] do
|
353 | |
tracpath = "/opt/rl/trac/#{@name}"
|
354 | |
|
355 | |
unless FileTest.exists?(tracpath)
|
356 | |
announce "No Trac instance at %s" % tracpath
|
357 | |
else
|
358 | |
output = %x{sudo trac-admin #{tracpath} version list}.chomp.split("\n")
|
359 | |
versions = {}
|
360 | |
output[3..-1].each do |line|
|
361 | |
name, time = line.chomp.split(/\s+/)
|
362 | |
versions[name] = time
|
363 | |
end
|
364 | |
|
365 | |
if versions.include?(@version)
|
366 | |
announce "Version #{@version} already in Trac"
|
367 | |
else
|
368 | |
announce "Adding #{@name} version #{@version} to Trac"
|
369 | |
date = [Time.now.year.to_s,
|
370 | |
Time.now.month.to_s,
|
371 | |
Time.now.day.to_s].join("-")
|
372 | |
system("sudo trac-admin #{tracpath} version add #{@version} #{date}")
|
373 | |
end
|
374 | |
end
|
375 | |
end
|
376 | |
end
|
377 | |
|
378 | |
# Create the tag task.
|
379 | |
def mktasktag
|
380 | |
desc "Tag all the files with the latest release number (REL=x.y.z)"
|
381 | |
task :tag => [:prerelease] do
|
382 | |
reltag = @version
|
383 | |
announce "Tagging with [#{reltag}]"
|
384 | |
|
385 | |
if ENV['RELTEST']
|
386 | |
announce "Release Task Testing, skipping tagging"
|
387 | |
else
|
388 | |
sh %{git tag #{reltag}}
|
389 | |
end
|
390 | |
end
|
391 | |
end
|
392 | |
|
393 | |
# Create the task for testing across all hosts.
|
394 | |
def mktaskhosttest
|
395 | |
desc "Test Puppet on each test host"
|
396 | |
task :hosttest do
|
397 | |
out = ""
|
398 | |
TESTHOSTS.each { |host|
|
399 | |
puts "testing %s" % host
|
400 | |
cwd = Dir.getwd
|
401 | |
file = "/tmp/#{@name}-#{host}test.out"
|
402 | |
system("ssh #{host} 'cd git/#{@name}/test; sudo rake' 2>&1 >#{file}")
|
403 | |
|
404 | |
if $? != 0
|
405 | |
puts "%s failed; output is in %s" % [host, file]
|
406 | |
end
|
407 | |
}
|
408 | |
end
|
409 | |
end
|
410 | |
|
411 | |
def mktaskri
|
412 | |
# Create a task to build the RDOC documentation tree.
|
413 | |
|
414 | |
#Rake::RDocTask.new("ri") { |rdoc|
|
415 | |
# #rdoc.rdoc_dir = 'html'
|
416 | |
# #rdoc.template = 'html'
|
417 | |
# rdoc.title = "Puppet"
|
418 | |
# rdoc.options << '--ri' << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
419 | |
# rdoc.rdoc_files.include('README', 'COPYING', 'CHANGELOG')
|
420 | |
# rdoc.rdoc_files.include('lib/**/*.rb', 'doc/**/*.rdoc')
|
421 | |
#}
|
422 | |
|
423 | |
if $features[:rdoc]
|
424 | |
task :ri do |ri|
|
425 | |
files = ['README', 'COPYING', 'CHANGELOG'] + Dir.glob('lib/**/*.rb')
|
426 | |
puts "files are \n%s" % files.join("\n")
|
427 | |
begin
|
428 | |
ri = RDoc::RDoc.new
|
429 | |
ri.document(["--ri-site"] + files)
|
430 | |
rescue RDoc::RDocError => detail
|
431 | |
puts "Failed to build docs: %s" % detail
|
432 | |
return nil
|
433 | |
rescue LoadError
|
434 | |
puts "Missing rdoc; cannot build documentation"
|
435 | |
return nil
|
436 | |
end
|
437 | |
end
|
438 | |
else
|
439 | |
warn "No rdoc; skipping ri."
|
440 | |
end
|
441 | |
end
|
442 | |
|
443 | |
desc "Install the application using the standard install.rb script"
|
444 | |
task :install do
|
445 | |
ruby "install.rb"
|
446 | |
end
|
447 | |
|
448 | |
def mktaskdefault
|
449 | |
if dtask = self.defaulttask
|
450 | |
desc "Default task"
|
451 | |
task :default => dtask
|
452 | |
end
|
453 | |
end
|
454 | |
|
455 | |
desc "Run all unit tests."
|
456 | |
task :alltests do
|
457 | |
if FileTest.exists?("spec/Rakefile")
|
458 | |
sh %{cd spec; rake}
|
459 | |
else
|
460 | |
Dir.chdir("spec") do
|
461 | |
Dir.entries(".").find_all { |f| f =~ /\.rb/ }.each do |f|
|
462 | |
sh %{ruby #{f}}
|
463 | |
end
|
464 | |
end
|
465 | |
end
|
466 | |
end
|
467 | |
|
468 | |
desc "List all ruby files"
|
469 | |
task :rubyfiles do
|
470 | |
puts Dir['**/*.rb'].reject { |fn| fn =~ /^pkg/ }
|
471 | |
puts Dir['**/bin/*'].reject { |fn| fn =~ /svn|(~$)|(\.rb$)/ }
|
472 | |
end
|
473 | |
|
474 | |
desc "Look for TODO and FIXME tags in the code"
|
475 | |
task :todo do
|
476 | |
egrep "/#.*(FIXME|TODO|TBD)/"
|
477 | |
end
|
478 | |
|
479 | |
# This task requires extra information from the Rake file.
|
480 | |
def mkgemtask
|
481 | |
# ====================================================================
|
482 | |
# Create a task that will package the Rake software into distributable
|
483 | |
# tar, zip and gem files.
|
484 | |
if ! defined?(Gem)
|
485 | |
puts "Package Target requires RubyGEMs"
|
486 | |
else
|
487 | |
spec = Gem::Specification.new { |s|
|
488 | |
|
489 | |
#### Basic information.
|
490 | |
|
491 | |
s.name = self.name
|
492 | |
s.version = self.version
|
493 | |
s.summary = self.summary
|
494 | |
s.description = self.description
|
495 | |
s.platform = Gem::Platform::RUBY
|
496 | |
|
497 | |
#### Dependencies and requirements.
|
498 | |
|
499 | |
# I'd love to explicitly list all of the libraries that I need,
|
500 | |
# but gems seem to only be able to handle dependencies on other
|
501 | |
# gems, which is, um, stupid.
|
502 | |
self.requires.each do |name, version|
|
503 | |
s.add_dependency(name, ">= #{version}")
|
504 | |
end
|
505 | |
|
506 | |
s.files = filelist.to_a
|
507 | |
|
508 | |
#### Signing key and cert chain
|
509 | |
#s.signing_key = '/..../gem-private_key.pem'
|
510 | |
#s.cert_chain = ['gem-public_cert.pem']
|
511 | |
|
512 | |
#### Author and project details.
|
513 | |
|
514 | |
s.author = [self.author]
|
515 | |
s.email = self.email
|
516 | |
s.homepage = self.url
|
517 | |
s.rubyforge_project = self.rfproject
|
518 | |
|
519 | |
yield s
|
520 | |
}
|
521 | |
|
522 | |
Rake::GemPackageTask.new(spec) { |pkg|
|
523 | |
pkg.need_tar = true
|
524 | |
}
|
525 | |
|
526 | |
desc "Copy the newly created package into the downloads directory"
|
527 | |
task :publish => [:package] do
|
528 | |
puts Dir.getwd
|
529 | |
sh %{cp pkg/#{@name}-#{@version}.gem #{self.publishdir}/gems}
|
530 | |
sh %{gem generate_index -d #{self.publishdir}}
|
531 | |
sh %{cp pkg/#{@name}-#{@version}.tgz #{self.pkgpublishdir}}
|
532 | |
sh %{ln -sf #{@name}-#{@version}.tgz #{self.pkgpublishdir}/#{@name}-latest.tgz}
|
533 | |
end
|
534 | |
CLEAN.include("pkg")
|
535 | |
end
|
536 | |
end
|
537 | |
end
|