summaryrefslogtreecommitdiffstats
path: root/tasks/rake/reductive.rb
blob: 4a3e18bc1174210c721af14393222d9921b758cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
#!/usr/bin/env ruby

# The tasks associated with building Reductive Labs projects

require 'rbconfig'
require 'rake'
require 'rake/tasklib'

require 'rake/clean'
require 'rake/testtask'

$features = {}

begin
    require 'rubygems'
    require 'rake/gempackagetask'
    $features[:gem] = true
rescue Exception
    $features[:gem] = false
    $stderr.puts "No Gems; skipping"
    nil
end

begin
    require 'rdoc/rdoc'
    $features[:rdoc] = true
rescue => detail
    $features[:rdoc] = false
    puts "No rdoc: %s" % detail
end

if $features[:rdoc]
    require 'rake/rdoctask'
end

# Create all of the standard targets for a Reductive Labs project.
# NOTE: The reason so many of the rake tasks are generated, rather than being
# declared directly, is that they need information from the project instance.
# Any rake task with an instance variable (e.g., @name or @version) needs
# to have that variable assigned *before* the task is defined.  Suckage.
class Rake::RedLabProject < Rake::TaskLib
    # The project name.
    attr_accessor :name

    # The project version.
    attr_accessor :version

    # The directory to which to publish packages and html and such.
    attr_accessor :publishdir

    # The package-specific publishing directory
    attr_accessor :pkgpublishdir

    # Create a Gem file.
    attr_accessor :mkgem

    # The hosts to run all of our tests on.
    attr_accessor :testhosts

    # The summary of this project.
    attr_accessor :summary

    # The description of this project.
    attr_accessor :description

    # The author of this project.
    attr_accessor :author

    # A Contact email address.
    attr_accessor :email

    # The URL for the project.
    attr_accessor :url

    # Where to get the source code.
    attr_accessor :source

    # Who the vendor is.
    attr_accessor :vendor

    # The copyright for this project
    attr_accessor :copyright

    # The RubyForge project.
    attr_accessor :rfproject

    # The list of files.  Only used for gem tasks.
    attr_writer :filelist

    # The directory in which to store packages. Defaults to "pkg".
    attr_accessor :package_dir

    # The default task.  Defaults to the 'alltests' task.
    attr_accessor :defaulttask

    # The defined requirements
    attr_reader :requires

    # The file containing the version string.
    attr_accessor :versionfile

    # Print messages on stdout
    def announce(msg = nil)
        puts msg
    end

    # Print messages on stderr
    def warn(msg = nil)
        $stderr.puts msg
    end

    def add_dependency(name, version)
        @requires[name] = version
    end

    # Where we'll be putting the code.
    def codedir
        unless defined? @codedir
            @codedir = File.join(self.package_dir, "#{@name}-#{@version}")
        end

        return @codedir
    end

    # Retrieve the current version from the code.
    def currentversion
        unless defined? @currentversion
            ver = %x{ruby -Ilib ./bin/#{@name} --version}.chomp
            if $? == 0 and ver != ""
                @currentversion = ver
            else
                warn "Could not retrieve current version; using 0.0.0"
                @currentversion = "0.0.0"
            end
        end

        return @currentversion
    end

    # Define all of our package tasks.  We just search through all of our
    # defined methods and call anything that's listed as making tasks.
    def define
        self.methods.find_all { |method| method.to_s =~ /^mktask/ }.each { |method|
            self.send(method)
        }
    end

    def egrep(pattern)
        Dir['**/*.rb'].each do |fn|
            count = 0
            open(fn) do |f|
                while line = f.gets
            count += 1
            if line =~ pattern
                puts "#{fn}:#{count}:#{line}"
            end
                end
            end
        end
    end

    # List all of the files.
    def filelist
        unless defined? @createdfilelist
            # If they passed in a file list as an array, then create a FileList
            # object out of it.
            if defined? @filelist
                unless @filelist.is_a? FileList
                    @filelist = FileList[@filelist]
                end
            else
                # Use a default file list.
                @filelist = FileList[
                    'install.rb',
                    '[A-Z]*',
                    'lib/**/*.rb',
                    'test/**/*.rb',
                    'bin/**/*',
                    'ext/**/*',
                    'examples/**/*',
                    'conf/**/*'
                ]
            end
            @filelist.delete_if {|item| item.include?(".git")}

            @createdfilelist = true
        end

        @filelist
    end

    def has?(feature)
        feature = feature.intern if feature.is_a? String
        if $features.include?(feature)
            return $features[feature]
        else
            return true
        end
    end

    def initialize(name, version = nil)
        @name = name

        if ENV['REL']
          @version = ENV['REL']
        else
          @version = version || self.currentversion
        end

        @defaulttask = :alltests
        @publishdir = ENV['DOWNLOAD_DIR'] || "/opt/rl/docroots/reductivelabs.com/htdocs/downloads"
        @pkgpublishdir = "#{@publishdir}/#{@name}"

        @email = "dev@reductivelabs.com"
        @url = "http://reductivelabs.com/projects/#{@name}"
        @source = "http://reductivelabs.com/downloads/#{@name}/#{@name}-#{@version}.tgz"
        @vendor = "Reductive Labs, LLC"
        @copyright = "Copyright 2003-2008, Reductive Labs, LLC. Some Rights Reserved."
        @rfproject = @name

        @defaulttask = :package

        @package_dir = "pkg"

        @requires = {}

        @versionfile = "lib/#{@name}.rb"

        CLOBBER.include('doc/*')

        yield self if block_given?
        define if block_given?
    end

    def mktaskhtml
        if $features[:rdoc]
            Rake::RDocTask.new(:html) { |rdoc|
                rdoc.rdoc_dir = 'html'
                rdoc.template = 'html'
                rdoc.title    = @name.capitalize
                rdoc.options << '--line-numbers' << '--inline-source' <<
                                '--main' << 'README'
                rdoc.rdoc_files.include('README', 'COPYING', 'CHANGELOG')
                rdoc.rdoc_files.include('lib/**/*.rb')
                CLEAN.include("html")
            }

            # Publish the html.
            task :publish => [:package, :html] do
                puts Dir.getwd
                apidocsdir = "#{@pkgpublishdir}/apidocs"
                File.makedirs(apidocsdir)
                sh %{cp -r html #{apidocsdir}}
            end
        else
            warn "No rdoc; skipping html"
        end
    end

    # Create a release task.
    def mktaskrelease
        desc "Make a new release"
        task :release => [
                :prerelease,
                :clobber,
                :update_version,
                :commit_newversion,
                :trac_version,
                :tag, # tag everything before we make a bunch of extra dirs
                :html,
                :package,
                :publish
              ] do
          
            announce 
            announce "**************************************************************"
            announce "* Release #{@version} Complete."
            announce "* Packages ready to upload."
            announce "**************************************************************"
            announce 
        end
    end

    # Do any prerelease work.
    def mktaskprerelease
        # Validate that everything is ready to go for a release.
        task :prerelease do
            announce 
            announce "**************************************************************"
            announce "* Making Release #{@version}"
            announce "* (current version #{self.currentversion})"
            announce "**************************************************************"
            announce  

            # Is a release number supplied?
            unless ENV['REL']
                warn "You must provide a release number when releasing"
                fail "Usage: rake release REL=x.y.z [REUSE=tag_suffix]"
            end

            # Is the release different than the current release.
            # (or is REUSE set?)
            if @version == self.currentversion && ! ENV['REUSE']
                fail "Current version is #{@version}, must specify REUSE=tag_suffix to reuse version"
            end

            # Are all source files checked in?
            if ENV['RELTEST']
                announce "Release Task Testing, skipping checked-in file test"
            else
                announce "Checking for unchecked-in files..."
                data = %x{git status}
                unless data.include?("nothing to commit")
                    fail "git status is not clean ... do you have unchecked-in files?"
                end
                announce "No outstanding checkins found ... OK"
            end
        end
    end

    # Create the task to update versions.
    def mktaskupdateversion
        task :update_version => [:prerelease] do
            if @version == self.currentversion
                announce "No version change ... skipping version update"
            else
                announce "Updating #{@versionfile} version to #{@version}"
                open(@versionfile) do |rakein|
                    open("#{@versionfile}.new", "w") do |rakeout|
                        rakein.each do |line|
                            if line =~ /^(\s*)#{@name.upcase}VERSION\s*=\s*/
                                rakeout.puts "#{$1}#{@name.upcase}VERSION = '#{@version}'"
                            else
                                rakeout.puts line
                            end
                        end
                    end
                end
                mv "#{@versionfile}.new", @versionfile

            end
        end

        desc "Commit the new versions to SVN."
        task :commit_newversion => [:update_version] do
            if ENV['RELTEST']
                announce "Release Task Testing, skipping commiting of new version"
            else
                sh %{git commit -m "Updated to version #{@version}" #{@versionfile}}
            end
        end
    end

    def mktasktrac_version
        task :trac_version => [:update_version] do
            tracpath = "/opt/rl/trac/#{@name}"

            unless FileTest.exists?(tracpath)
                announce "No Trac instance at %s" % tracpath
            else
                output = %x{sudo trac-admin #{tracpath} version list}.chomp.split("\n")
                versions = {}
                output[3..-1].each do |line|
                    name, time = line.chomp.split(/\s+/)
                    versions[name] = time
                end

                if versions.include?(@version)
                    announce "Version #{@version} already in Trac"
                else
                    announce "Adding #{@name} version #{@version} to Trac"
                    date = [Time.now.year.to_s,
                        Time.now.month.to_s,
                        Time.now.day.to_s].join("-")
                    system("sudo trac-admin #{tracpath} version add #{@version} #{date}")
                end
            end
        end
    end

    # Create the tag task.
    def mktasktag
        desc "Tag all the files with the latest release number (REL=x.y.z)"
        task :tag => [:prerelease] do
            reltag = @version
            announce "Tagging with [#{reltag}]"

            if ENV['RELTEST']
                announce "Release Task Testing, skipping tagging"
            else
                sh %{git tag #{reltag}}
            end
        end
    end

    # Create the task for testing across all hosts.
    def mktaskhosttest
        desc "Test Puppet on each test host"
        task :hosttest do
            out = ""
            TESTHOSTS.each { |host|
                puts "testing %s" % host
                cwd = Dir.getwd
                file = "/tmp/#{@name}-#{host}test.out"
                system("ssh #{host} 'cd git/#{@name}/test; sudo rake' 2>&1 >#{file}")

                if $? != 0
                    puts "%s failed; output is in %s" % [host, file]
                end
            }
        end
    end

    def mktaskri
        # Create a task to build the RDOC documentation tree.

        #Rake::RDocTask.new("ri") { |rdoc|
        #    #rdoc.rdoc_dir = 'html'
        #    #rdoc.template = 'html'
        #    rdoc.title    = "Puppet"
        #    rdoc.options << '--ri' << '--line-numbers' << '--inline-source' << '--main' << 'README'
        #    rdoc.rdoc_files.include('README', 'COPYING', 'CHANGELOG')
        #    rdoc.rdoc_files.include('lib/**/*.rb', 'doc/**/*.rdoc')
        #}

        if $features[:rdoc]
            task :ri do |ri|
                files = ['README', 'COPYING', 'CHANGELOG'] + Dir.glob('lib/**/*.rb')
                puts "files are \n%s" % files.join("\n")
                begin
                    ri = RDoc::RDoc.new
                    ri.document(["--ri-site"] + files)
                rescue RDoc::RDocError => detail
                    puts "Failed to build docs: %s" % detail
                    return nil
                rescue LoadError
                    puts "Missing rdoc; cannot build documentation"
                    return nil
                end
            end
        else
            warn "No rdoc; skipping ri."
        end
    end

    desc "Install the application using the standard install.rb script"
    task :install do
        ruby "install.rb"
    end

    def mktaskdefault
        if dtask = self.defaulttask
            desc "Default task"
            task :default => dtask
        end
    end

    desc "Run all unit tests."
    task :alltests do
        if FileTest.exists?("spec/Rakefile")
            sh %{cd spec; rake}
        else
            Dir.chdir("spec") do
                Dir.entries(".").find_all { |f| f =~ /\.rb/ }.each do |f|
                    sh %{ruby #{f}}
                end
            end
        end
    end

    desc "List all ruby files"
    task :rubyfiles do 
        puts Dir['**/*.rb'].reject { |fn| fn =~ /^pkg/ }
        puts Dir['**/bin/*'].reject { |fn| fn =~ /svn|(~$)|(\.rb$)/ }
    end

    desc "Look for TODO and FIXME tags in the code"
    task :todo do
        egrep "/#.*(FIXME|TODO|TBD)/"
    end

    # This task requires extra information from the Rake file.
    def mkgemtask
        # ====================================================================
        # Create a task that will package the Rake software into distributable
        # tar, zip and gem files.
        if ! defined?(Gem)
            puts "Package Target requires RubyGEMs"
        else
            spec = Gem::Specification.new { |s|

                #### Basic information.

                s.name = self.name
                s.version = self.version
                s.summary = self.summary
                s.description = self.description
                s.platform = Gem::Platform::RUBY

                #### Dependencies and requirements.

                # I'd love to explicitly list all of the libraries that I need,
                # but gems seem to only be able to handle dependencies on other
                # gems, which is, um, stupid.
                self.requires.each do |name, version|
                    s.add_dependency(name, ">= #{version}")
                end

                s.files = filelist.to_a

                #### Signing key and cert chain
                #s.signing_key = '/..../gem-private_key.pem'
                #s.cert_chain = ['gem-public_cert.pem']

                #### Author and project details.

                s.author = [self.author]
                s.email = self.email
                s.homepage = self.url
                s.rubyforge_project = self.rfproject

                yield s
            }

            Rake::GemPackageTask.new(spec) { |pkg|
                pkg.need_tar = true
            }

            desc "Copy the newly created package into the downloads directory"
            task :publish => [:package] do
                puts Dir.getwd
                sh %{cp pkg/#{@name}-#{@version}.gem #{self.publishdir}/gems}
                sh %{gem generate_index -d #{self.publishdir}}
                sh %{cp pkg/#{@name}-#{@version}.tgz #{self.pkgpublishdir}}
                sh %{ln -sf #{@name}-#{@version}.tgz #{self.pkgpublishdir}/#{@name}-latest.tgz}
            end
            CLEAN.include("pkg")
        end
    end
end