diff --git a/Rakefile b/Rakefile index 21d7651d4..16c6488cf 100644 --- a/Rakefile +++ b/Rakefile @@ -1,228 +1,230 @@ # Rakefile for Puppet -*- ruby -*- +$LOAD_PATH << File.join(File.dirname(__FILE__), 'tasks') + $: << File.expand_path(File.join(File.dirname(__FILE__), 'lib')) begin require 'rake/reductive' rescue LoadError $stderr.puts "You must have the Reductive build library in your RUBYLIB; see http://github.com/lak/reductive-build/tree/master." exit(14) end TESTHOSTS = %w{rh3a fedora1 centos1 freebsd1 culain} project = Rake::RedLabProject.new("puppet") do |p| p.summary = "System Automation and Configuration Management Software" p.description = "Puppet is a declarative language for expressing system configuration, a client and server for distributing it, and a library for realizing the configuration." p.filelist = [ 'install.rb', '[A-Z]*', 'lib/puppet.rb', 'lib/puppet/**/*.rb', 'lib/puppet/**/*.py', 'test/**/*', 'spec/**/*', 'bin/**/*', 'ext/**/*', 'examples/**/*', 'conf/**/*', 'man/**/*' ] p.filelist.exclude("bin/pi") p.add_dependency('facter', '1.1.0') end if project.has?(:gem) # Make our gem task. This actually just fills out the spec. project.mkgemtask do |task| task.require_path = 'lib' # Use these for libraries. task.bindir = "bin" # Use these for applications. task.executables = ["puppet", "puppetd", "puppetmasterd", "puppetdoc", "puppetca", "puppetrun", "ralsh"] task.default_executable = "puppet" #### Documentation and testing. task.has_rdoc = true #s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a task.rdoc_options << '--title' << 'Puppet - Configuration Management' << '--main' << 'README' << '--line-numbers' task.test_file = "test/Rakefile" task.author = "Luke Kanies" end end rule(/_is_runnable$/) do |t| available = false executable = t.name.sub(/_is_runnable$/, '') ENV['PATH'].split(':').each do |elem| available = true if File.executable? File.join(elem, executable) end unless available puts "You do not have #{executable} available in your path" exit 1 end end task :check_build_deps => 'dpkg-checkbuilddeps_is_runnable' do system("dpkg-checkbuilddeps") || exit(1) end task :debian_packages => [ "debian", :check_build_deps, :fakeroot_is_runnable ] do system("fakeroot debian/rules clean") || exit(1) system("fakeroot debian/rules binary") || exit(1) end def dailyfile(package) "#{downdir}/#{package}/#{package}-daily-#{stamp}.tgz" end def daily(package) edir = "/tmp/daily-export" Dir.mkdir edir Dir.chdir(edir) do sh %{git clone git://reductivelabs.com/#{package} #{package} >/dev/null} sh %{tar cf - #{package} | gzip -c > #{dailyfile(package)}} end FileUtils.rm_rf(edir) end def downdir ENV['DOWNLOAD_DIR'] || "/opt/rl/docroots/reductivelabs.com/htdocs/downloads" end def stamp [Time.now.year, Time.now.month, Time.now.day].collect { |i| i.to_s}.join end pdaily = dailyfile("puppet") fdaily = dailyfile("facter") file pdaily do daily("puppet") end file fdaily do daily("facter") end task :daily => [pdaily, fdaily] task :dailyclean do Dir.glob("#{downdir}/*/*daily*.tgz").each do |file| puts "Removing %s" % file File.unlink(file) end end task :tracdocs do require 'puppet' require 'puppet/util/reference' Puppet::Util::Reference.references.each do |ref| sh "puppetdoc -m trac -r #{ref.to_s}" end end desc "Run the specs under spec/" task :spec do require 'spec' require 'spec/rake/spectask' # require 'rcov' Spec::Rake::SpecTask.new do |t| # t.rcov = true t.spec_opts = ['--format','s', '--loadby','mtime'] t.spec_files = FileList['spec/**/*.rb'] end end desc "Run the unit tests" task :unit do sh "cd test; rake" end namespace :ci do desc "Run the CI prep tasks" task :prep do require 'rubygems' gem 'ci_reporter' require 'ci/reporter/rake/rspec' require 'ci/reporter/rake/test_unit' ENV['CI_REPORTS'] = 'results' end desc "Run CI Unit tests" task :unit => [:prep, 'ci:setup:testunit'] do sh "cd test; rake test; exit 0" end desc "Run CI RSpec tests" task :spec => [:prep, 'ci:setup:rspec'] do sh "cd spec; rake all; exit 0" end end desc "Send patch information to the puppet-dev list" task :mail_patches do if Dir.glob("00*.patch").length > 0 raise "Patches already exist matching '00*.patch'; clean up first" end unless %x{git status} =~ /On branch (.+)/ raise "Could not get branch from 'git status'" end branch = $1 unless branch =~ %r{^([^\/]+)/([^\/]+)/([^\/]+)$} raise "Branch name does not follow // model; cannot autodetect parent branch" end type, parent, name = $1, $2, $3 # Create all of the patches sh "git-format-patch -C -M -s -n #{parent}..HEAD" # And then mail them out. # If we've got more than one patch, add --compose if Dir.glob("00*.patch").length > 1 compose = "--compose" else compose = "" end # Now send the mail. sh "git send-email #{compose} --no-chain-reply-to --no-signed-off-by-cc --suppress-from --no-thread --to puppet-dev@googlegroups.com 00*.patch" # Finally, clean up the patches sh "rm 00*.patch" end desc "Create a changelog based on your git commits." task :changelog do CHANGELOG_DIR = "#{Dir.pwd}" mkdir(CHANGELOG_DIR) unless File.directory?(CHANGELOG_DIR) change_body=`git log --pretty=format:'%aD%n%an <%ae>%n%s%n'` File.open(File.join(CHANGELOG_DIR, "CHANGELOG.git"), 'w') do |f| f << change_body end # Changelog commit `git add #{CHANGELOG_DIR}/CHANGELOG.git` `git commit -m "Update CHANGELOG.git"` end diff --git a/tasks/rake/redlabpackage.rb b/tasks/rake/redlabpackage.rb new file mode 100644 index 000000000..2de8005b6 --- /dev/null +++ b/tasks/rake/redlabpackage.rb @@ -0,0 +1,265 @@ +#!/usr/bin/env ruby + +# A raw platform for creating packages. + +require 'rbconfig' +require 'rake' +require 'rake/tasklib' + +# The PackageTask will create the following targets: +# +# [:clobber_package] +# Delete all the package files. This target is automatically +# added to the main clobber target. +# +# [:repackage] +# Rebuild the package files from scratch, even if they are not out +# of date. +# +# ["package_dir/name-version.tgz"] +# Create a gzipped tar package (if need_tar is true). +# +# ["package_dir/name-version.tar.gz"] +# Create a gzipped tar package (if need_tar_gz is true). +# +# ["package_dir/name-version.tar.bz2"] +# Create a bzip2'd tar package (if need_tar_bz2 is true). +# +# ["package_dir/name-version.zip"] +# Create a zip package archive (if need_zip is true). +# +# Example: +# +# Rake::PackageTask.new("rake", "1.2.3") do |p| +# p.need_tar = true +# p.package_files.include("lib/**/*.rb") +# end +# +class Rake::RedLabPackageTask < Rake::TaskLib + # The different directory types we can manage. + DIRTYPES = { + :bindir => :bins, + :sbindir => :sbins, + :sitelibdir => :rubylibs + } + + # Name of the package (from the GEM Spec). + attr_accessor :name + + # Version of the package (e.g. '1.3.2'). + attr_accessor :version + + # Directory used to store the package files (default is 'pkg'). + attr_accessor :package_dir + + # The directory to which to publish packages and html and such. + attr_accessor :publishdir + + # The package-specific publishing directory + attr_accessor :pkgpublishdir + + # The Product name. Defaults to a capitalized version of the + # package name + attr_accessor :product + + # The copyright message. + attr_accessor :copyright + + # The vendor. + attr_accessor :vendor + + # The license file. Defaults to COPYING. + attr_accessor :license + + # The readme file. Defaults to README. + attr_accessor :readme + + # The description. + attr_accessor :description + + # The summary. + attr_accessor :summary + + # The directory in which to put the binaries. Defaults to the system + # default. + attr_accessor :bindir + + # The executables. + attr_accessor :bins + + # The directory in which to put the system binaries. Defaults to the + # system default. + attr_accessor :sbindir + + # The system binaries. + attr_accessor :sbins + + # The libraries. + attr_accessor :rubylibs + + # The directory in which to put Ruby libraries. Defaults to the + # system site_dir. + attr_accessor :sitelibdir + + # The URL for the package. + attr_accessor :url + + # The source for the package. + attr_accessor :source + + # Our operating system. + attr_reader :os + + # Add a required package. + def add_dependency(name, version = nil) + @requires[name] = version + end + + # Create the tasks defined by this task library. + def define + fail "Version required (or :noversion)" if @version.nil? + @version = nil if :noversion == @version + + directory pkgdest + file pkgdest => self.package_dir + + directory self.package_dir + + self.mkcopytasks + + self + end + + # Return the list of files associated with a dirname. + def files(dirname) + if @dirtypes.include?(dirname) + return self.send(@dirtypes[dirname]) + else + raise "Could not find directory type %s" % dirname + end + end + + # Create a Package Task with the given name and version. + def initialize(name=nil, version=nil) + # Theoretically, one could eventually add directory types here. + @dirtypes = DIRTYPES.dup + + @requires = {} + + @name = name + @version = version + @package_dir = 'pkg' + @product = name.capitalize + + @bindir = Config::CONFIG["bindir"] + @sbindir = Config::CONFIG["sbindir"] + @sitelibdir = Config::CONFIG["sitelibdir"] + + @license = "COPYING" + @readme = "README" + + yield self if block_given? + + define unless name.nil? + + # Make sure they've provided everything necessary. + %w{copyright vendor description}.each do |attr| + unless self.send(attr) + raise "You must provide the attribute %s" % attr + end + end + end + + # Make tasks for copying/linking all of the necessary files. + def mkcopytasks + basedir = pkgdest() + + tasks = [] + + # Iterate across all of the file locations... + @dirtypes.each do |dirname, filemethod| + tname = ("copy" + dirname.to_s).intern + + dir = self.send(dirname) + + reqs = [] + + # This is where we're putting the files. + targetdir = self.targetdir(dirname) + + # Make sure our target directories exist + directory targetdir + file targetdir => basedir + + # Get the file list and remove the leading directory. + files = self.files(dirname) or next + + reqs = [] + files.each do |sourcefile| + # The file without the basedir. This is necessary because + # files are created with the path from ".", but they often + # have 'lib' changed to 'site_ruby' or something similar. + destfile = File.join(targetdir, sourcefile.sub(/^\w+\//, '')) + reqs << destfile + + # Make sure the base directory is listed as a prereq + sourcedir = File.dirname(sourcefile) + destdir = nil + unless sourcedir == "." + destdir = File.dirname(destfile) + reqs << destdir + directory(destdir) + end + + # Now make the task associated with creating the object in + # question. + if FileTest.directory?(sourcefile) + directory(destfile) + else + file(destfile => sourcefile) do + if FileTest.exists?(destfile) + if File.stat(sourcefile) > File.stat(destfile) + rm_f destfile + safe_ln(sourcefile, destfile) + end + else + safe_ln(sourcefile, destfile) + end + end + + # If we've set the destdir, then list it as a prereq. + if destdir + file destfile => destdir + end + end + end + + # And create a task for each one + task tname => reqs + + # And then mark our task as a prereq + tasks << tname + end + + task :copycode => [self.package_dir, pkgdest] + + task :copycode => tasks do + puts "Finished copying" + end + end + + # Where we're copying a given type of file. + def targetdir(dirname) + File.join(pkgdest(), self.send(dirname)).sub("//", "/") + end + + private + + def package_name + @version ? "#{@name}-#{@version}" : @name + end + + def package_dir_path + "#{package_dir}/#{package_name}" + end +end diff --git a/tasks/rake/reductive.rb b/tasks/rake/reductive.rb new file mode 100644 index 000000000..2fbd2b4d9 --- /dev/null +++ b/tasks/rake/reductive.rb @@ -0,0 +1,538 @@ +#!/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 = "/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 + sh %{cp -r html #{self.pkgpublishdir}/apidocs} + 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?("test/Rakefile") + sh %{cd test; rake} + else + Dir.chdir("test") 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