Please review pull request #245: Build a Rake task for building Apple Packages opened by (glarizza)
Description:
The goal is to have our release managers build all Puppet packages
within Rake, and so this commit adds a rake task (apple_package) that
will build a DMG-encapsulated package for OS X and put it into the
pkg/apple directory off of the Puppet Root.
To accomplish this goal, a folder structure is created in
/tmp/puppet/puppet-#{version} that mirrors the structure needed for
Apple's Packagemaker CLI tool to build a package for OS X. Next, the
necessary files are copied from the Puppet source into the structure in
/tmp/puppet/puppet-#{version} and then packagemaker is run
(targeting that folder structure) to build an initial package. Hdiutil
then encapsulates that package into a DMG, and the file is finally
copied into the pkg/apple directory.
- Opened: Mon Dec 05 22:38:07 UTC 2011
- Based on: puppetlabs:master (eb0db02f463df166fd7a8d14d126ab98364f3c66)
- Requested merge: glarizza:feature/master/Rakefile_DMG (2ef68f67259cada1117c21996370709309ad5edd)
Diff follows:
diff --git a/tasks/rake/apple.rake b/tasks/rake/apple.rake new file mode 100644 index 0000000..4bf0ca3 --- /dev/null +++ b/tasks/rake/apple.rake @@ -0,0 +1,164 @@ +# Title: Rake task to build Apple packages for Puppet. +# Author: Gary Larizza +# Date: 12/5/2011 +# Description: This task will create a DMG-encapsulated package that will +# install Puppet on OS X systems. This happens by building +# a directory tree of files that will then be fed to the +# packagemaker binary (can be installed by installing the +# XCode Tools) which will create the .pkg file. +# +require 'fileutils' +require 'erb' +require 'find' +require 'pathname' + +# Path to Binaries (Constants) +TAR = '/usr/bin/tar' +CP = '/bin/cp' +INSTALL = '/usr/bin/install' +DITTO = '/usr/bin/ditto' +PACKAGEMAKER = '/Developer/usr/bin/packagemaker' +SED = '/usr/bin/sed' + +# Class Instance Variables +@title = "puppet-#{Puppet::PUPPETVERSION}" +@reverse_domain = 'com.puppetlabs.puppet' +@package_major_version = Puppet::PUPPETVERSION.split('.')[0] +@package_minor_version = Puppet::PUPPETVERSION.split('.')[1] + + Puppet::PUPPETVERSION.split('.')[2] + +# Template-specific Variables +@pm_restart = 'None' +@build_date = Time.new.strftime("%Y-%m-%dT%H:%M:%SZ") + +# method: make_directory_tree +# description: This method sets up the directory structure that packagemaker +# needs to build a package. A prototype.plist file (holding +# package-specific options) is built from an ERB template located +# in the tasks/rake/templates directory. +def make_directory_tree + puppet_tmp = '/tmp/puppet' + @scratch = "#{puppet_tmp}/#{@title}" + @working_tree = { + 'scripts' => "#{@scratch}/scripts", + 'resources' => "#{@scratch}/resources", + 'working' => "#{@scratch}/root", + 'payload' => "#{@scratch}/payload", + } + puts "Cleaning Tree: #{puppet_tmp}" + FileUtils.rm_rf(puppet_tmp) + @working_tree.each do |key,val| + puts "Creating: #{val}" + FileUtils.mkdir_p(val) + end + File.open("#{@scratch}/#{'prototype.plist'}", "w+") do |f| + f.write(ERB.new(File.read('tasks/rake/templates/prototype.plist.erb')).result()) + end +end + +# method: build_dmg +# description: This method builds a package from the directory structure in +# /tmp/puppet and puts it in the +# /tmp/puppet/puppet-#{version}/payload directory. A DMG is +# created, using hdiutil, based on the contents of the +# /tmp/puppet/puppet-#{version}/payload directory. The resultant +# DMG is placed in the pkg/apple directory. +# +def build_dmg + # Local Variables + dmg_format_code = 'UDZO' + zlib_level = '9' + dmg_format_option = "-imagekey zlib-level=#{zlib_level}" + dmg_format = "#{dmg_format_code} #{dmg_format_option}" + dmg_file = "#{@title}.dmg" + package_file = "#{@title}.pkg" + pm_extra_args = '--verbose --no-recommend --no-relocate' + package_target_os = '10.4' + + # Build .pkg file + system("sudo #{PACKAGEMAKER} --root #{@working_tree['working']} \ + --id #{@reverse_domain} \ + --filter DS_Store \ + --target #{package_target_os} \ + --title #{@title} \ + --info #{@scratch}/prototype.plist \ + --scripts #{@working_tree['scripts']} \ + --resources #{@working_tree['resources']} \ + --version #{Puppet::PUPPETVERSION} \ + #{pm_extra_args} --out #{@working_tree['payload']}/#{package_file}") + + # Build .dmg file + system("sudo hdiutil create -volname #{@title} \ + -srcfolder #{@working_tree['payload']} \ + -uid 99 \ + -gid 99 \ + -ov \ + -format #{dmg_format} \ + #{dmg_file}") + + if File.directory?("#{Pathname.pwd}/pkg/apple") + FileUtils.mv("#{Pathname.pwd}/#{dmg_file}", "#{Pathname.pwd}/pkg/apple/#{dmg_file}") + else + FileUtils.mkdir_p("#{Pathname.pwd}/pkg/apple") + FileUtils.mv(dmg_file, "#{Pathname.pwd}/pkg/apple/#{dmg_file}") + end +end + +# method: pack_puppet_source +# description: This method copies the puppet source into a directory +# structure in /tmp/puppet/puppet-#{version}/root mirroring the +# structure on the target system for which the package will be +# installed. Anything installed into /tmp/puppet/root will be +# installed as the package's payload. +# +def pack_puppet_source + work = "#{@working_tree['working']}" + puppet_source = Pathname.pwd + + # Make all necessary directories + directories = ["#{work}/private/etc/puppet/", + "#{work}/usr/bin", + "#{work}/usr/sbin", + "#{work}/usr/share/doc/puppet", + "#{work}/usr/share/man/man5", + "#{work}/usr/share/man/man8", + "#{work}/Library/Ruby/Site/1.8/puppet"] + FileUtils.mkdir_p(directories) + + # Install necessary files + system("#{INSTALL} -o root -g wheel -m 644 #{puppet_source}/conf/auth.conf #{work}/private/etc/puppet/auth.conf") + system("#{DITTO} #{puppet_source}/bin/ #{work}/usr/bin") + system("#{DITTO} #{puppet_source}/sbin/ #{work}/usr/sbin") + system("#{INSTALL} -o root -g wheel -m 644 #{puppet_source}/man/man5/puppet.conf.5 #{work}/usr/share/man/man5/") + system("#{DITTO} #{puppet_source}/man/man8/ #{work}/usr/share/man/man8") + system("#{DITTO} #{puppet_source}/lib/ #{work}/Library/Ruby/Site/1.8/") + + # Setup a preflight script and replace variables in the files with + # the correct paths. + system("#{INSTALL} -o root -g wheel -m 644 #{puppet_source}/conf/osx/preflight #{@working_tree['scripts']}") + system("#{SED} -i '' \"s\#{SITELIBDIR}\#/usr/lib/ruby/site_ruby/1.8\#g\" #{@working_tree['scripts']}/preflight") + system("#{SED} -i '' \"s\#{BINDIR}\#/usr/bin\#g\" #{@working_tree['scripts']}/preflight") + + # Install documentation (matching for files with capital letters) + Dir.foreach("#{puppet_source}") do |file| + system("#{INSTALL} -o root -g wheel -m 644 #{puppet_source}/#{file} #{work}/usr/share/doc/puppet") if file =~ /^[A-Z][A-Z]/ + end + + # Set Permissions + executable_directories = [ "#{work}/usr/bin", + "#{work}/usr/sbin", + "#{work}/usr/share/man/man8"] + FileUtils.chmod_R(0755, executable_directories) + FileUtils.chown_R('root', 'wheel', directories) + FileUtils.chmod_R(0644, "#{work}/Library/Ruby/Site/1.8/") + FileUtils.chown_R('root', 'wheel', "#{work}/Library/Ruby/Site/1.8/") + Find.find("#{work}/Library/Ruby/Site/1.8/") do |dir| + FileUtils.chmod(0755, dir) if File.directory?(dir) + end +end + +task :apple_package do + make_directory_tree + pack_puppet_source + build_dmg +end diff --git a/tasks/rake/templates/prototype.plist.erb b/tasks/rake/templates/prototype.plist.erb new file mode 100644 index 0000000..36bea53 --- /dev/null +++ b/tasks/rake/templates/prototype.plist.erb @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleIdentifier</key> + <string><%= @title %></string> + <key>CFBundleShortVersionString</key> + <string><%= Puppet::PUPPETVERSION %></string> + <key>IFMajorVersion</key> + <integer><%= @package_major_version %></integer> + <key>IFMinorVersion</key> + <integer><%= @package_minor_version %></integer> + <key>IFPkgBuildDate</key> + <date><%= @build_date %></date> + <key>IFPkgFlagAllowBackRev</key> + <false/> + <key>IFPkgFlagAuthorizationAction</key> + <string>RootAuthorization</string> + <key>IFPkgFlagDefaultLocation</key> + <string>/</string> + <key>IFPkgFlagFollowLinks</key> + <true/> + <key>IFPkgFlagInstallFat</key> + <false/> + <key>IFPkgFlagIsRequired</key> + <false/> + <key>IFPkgFlagOverwritePermissions</key> + <false/> + <key>IFPkgFlagRelocatable</key> + <false/> + <key>IFPkgFlagRestartAction</key> + <string><%= @pm_restart %></string> + <key>IFPkgFlagRootVolumeOnly</key> + <true/> + <key>IFPkgFlagUpdateInstalledLanguages</key> + <false/> +</dict> +</plist>
--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To post to this group, send email to puppet-dev@googlegroups.com.
To unsubscribe from this group, send email to puppet-dev+unsubscr...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.