├── .gitignore ├── LICENSE.md ├── README.md └── brew-pkg.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.pkg 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Timothy Sutton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # brew-pkg 2 | 3 | brew-pkg is a Homebrew external command that builds an OS X installer package from a formula. The formula must first already be installed on the system. 4 | 5 | ## Usage 6 | 7 | Assuming nginx is already installed: 8 | 9 | `brew pkg nginx` 10 |
==> Creating package staging root using Homebrew prefix /usr/local
11 | ==> Staging formula nginx
12 | ==> Plist found at homebrew.mxcl.nginx, staging for /Library/LaunchDaemons/homebrew.mxcl.nginx.plist
13 | ==> Building package nginx-1.2.6.pkg
14 | 15 | It can also automatically include the formula's dependencies: 16 | 17 | `brew pkg --with-deps --without-kegs ffmpeg` 18 |
==> Creating package staging root using Homebrew prefix /usr/local
19 | ==> Staging formula ffmpeg
20 | ==> Staging formula pkg-config
21 | ==> Staging formula texi2html
22 | ==> Staging formula yasm
23 | ==> Staging formula x264
24 | ==> Staging formula faac
25 | ==> Staging formula lame
26 | ==> Staging formula xvid
27 | ==> Building package ffmpeg-1.1.pkg
28 | 29 | By default behaviour brew pkg include all package kegs located in /usr/local/Cellar/packagename. If you need to exclude it, specify option --without-kegs 30 | 31 | ## Installing it 32 | 33 | brew-pkg is available from my [formulae tap](https://github.com/timsutton/homebrew-formulae). Add the tap: 34 | 35 | `brew tap timsutton/formulae` 36 | 37 | Then install as any other formula: 38 | 39 | `brew install brew-pkg` 40 | 41 | ## Extras 42 | 43 | If a formula has defined a launchd plist, brew-pkg will also install this to the package's root in `/Library/LaunchDaemons`. 44 | 45 | You can also define a custom identifier prefix in the reverse-domain convention with the `--identifier-prefix` option, ie. `brew pkg --identifier-prefix org.nagios nrpe`. If there is a launchd plist defined, this same prefix is currently _not_ applied to the plist. 46 | 47 | You can set the path to custom preinstall and postinstall scripts with the `--scripts` option which is just literally passed through to the `pkgbuild` command. 48 | For more information refer to `man pkgbuild` which explains that *`--scripts scripts_path` archive the entire contents of scripts-path as the package scripts. If this directory contains scripts named preinstall and/or postinstall, these will be run as the top-level scripts of the package [...]*. 49 | 50 | ## License 51 | 52 | brew-pkg is [MIT-licensed](https://github.com/timsutton/brew-pkg/blob/master/LICENSE.md). 53 | -------------------------------------------------------------------------------- /brew-pkg.rb: -------------------------------------------------------------------------------- 1 | # Builds an OS X installer package from an installed formula. 2 | require 'formula' 3 | require 'optparse' 4 | require 'tmpdir' 5 | 6 | module HomebrewArgvExtension extend self 7 | def with_deps? 8 | flag? '--with-deps' 9 | end 10 | end 11 | 12 | # cribbed Homebrew module code from brew-unpack.rb 13 | module Homebrew extend self 14 | def pkg 15 | unpack_usage = <<-EOS 16 | Usage: brew pkg [--identifier-prefix] [--with-deps] [--without-kegs] formula 17 | 18 | Build an OS X installer package from a formula. It must be already 19 | installed; 'brew pkg' doesn't handle this for you automatically. The 20 | '--identifier-prefix' option is strongly recommended in order to follow 21 | the conventions of OS X installer packages. 22 | 23 | Options: 24 | --identifier-prefix set a custom identifier prefix to be prepended 25 | to the built package's identifier, ie. 'org.nagios' 26 | makes a package identifier called 'org.nagios.nrpe' 27 | --with-deps include all the package's dependencies in the built package 28 | --without-kegs exclude package contents at /usr/local/Cellar/packagename 29 | --scripts set the path to custom preinstall and postinstall scripts 30 | EOS 31 | 32 | abort unpack_usage if ARGV.empty? 33 | identifier_prefix = if ARGV.include? '--identifier-prefix' 34 | ARGV.next.chomp(".") 35 | else 36 | 'org.homebrew' 37 | end 38 | 39 | f = Formulary.factory ARGV.last 40 | # raise FormulaUnspecifiedError if formulae.empty? 41 | # formulae.each do |f| 42 | name = f.name 43 | identifier = identifier_prefix + ".#{name}" 44 | version = f.version.to_s 45 | version += "_#{f.revision}" if f.revision.to_s != '0' 46 | 47 | # Make sure it's installed first 48 | if not f.any_version_installed? 49 | onoe "#{f.name} is not installed. First install it with 'brew install #{f.name}'." 50 | abort 51 | end 52 | 53 | # Setup staging dir 54 | pkg_root = Dir.mktmpdir 'brew-pkg' 55 | staging_root = pkg_root + HOMEBREW_PREFIX 56 | ohai "Creating package staging root using Homebrew prefix #{HOMEBREW_PREFIX}" 57 | FileUtils.mkdir_p staging_root 58 | 59 | 60 | pkgs = [f] 61 | 62 | # Add deps if we specified --with-deps 63 | pkgs += f.recursive_dependencies if ARGV.include? '--with-deps' 64 | 65 | pkgs.each do |pkg| 66 | formula = Formulary.factory(pkg.to_s) 67 | dep_version = formula.version.to_s 68 | dep_version += "_#{formula.revision}" if formula.revision.to_s != '0' 69 | 70 | 71 | ohai "Staging formula #{formula.name}" 72 | # Get all directories for this keg, rsync to the staging root 73 | 74 | if File.exist?(File.join(HOMEBREW_CELLAR, formula.name, dep_version)) 75 | 76 | dirs = Pathname.new(File.join(HOMEBREW_CELLAR, formula.name, dep_version)).children.select { |c| c.directory? }.collect { |p| p.to_s } 77 | 78 | 79 | dirs.each {|d| safe_system "rsync", "-a", "#{d}", "#{staging_root}/" } 80 | 81 | 82 | if File.exist?("#{HOMEBREW_CELLAR}/#{formula.name}/#{dep_version}") and not ARGV.include? '--without-kegs' 83 | 84 | ohai "Staging directory #{HOMEBREW_CELLAR}/#{formula.name}/#{dep_version}" 85 | 86 | safe_system "mkdir", "-p", "#{staging_root}/Cellar/#{formula.name}/" 87 | safe_system "rsync", "-a", "#{HOMEBREW_CELLAR}/#{formula.name}/#{dep_version}", "#{staging_root}/Cellar/#{formula.name}/" 88 | end 89 | 90 | end 91 | 92 | # Write out a LaunchDaemon plist if we have one 93 | if formula.service? 94 | ohai "Plist found at #{formula.plist_name}, staging for /Library/LaunchDaemons/#{formula.plist_name}.plist" 95 | launch_daemon_dir = File.join staging_root, "Library", "LaunchDaemons" 96 | FileUtils.mkdir_p launch_daemon_dir 97 | fd = File.new(File.join(launch_daemon_dir, "#{formula.plist_name}.plist"), "w") 98 | fd.write formula.service.to_plist 99 | fd.close 100 | end 101 | end 102 | 103 | # Add scripts if we specified --scripts 104 | found_scripts = false 105 | if ARGV.include? '--scripts' 106 | scripts_path = ARGV.next 107 | if File.directory?(scripts_path) 108 | pre = File.join(scripts_path,"preinstall") 109 | post = File.join(scripts_path,"postinstall") 110 | if File.exist?(pre) 111 | File.chmod(0755, pre) 112 | found_scripts = true 113 | ohai "Adding preinstall script" 114 | end 115 | if File.exist?(post) 116 | File.chmod(0755, post) 117 | found_scripts = true 118 | ohai "Adding postinstall script" 119 | end 120 | end 121 | if not found_scripts 122 | opoo "No scripts found in #{scripts_path}" 123 | end 124 | end 125 | 126 | # Custom ownership 127 | found_ownership = false 128 | if ARGV.include? '--ownership' 129 | custom_ownership = ARGV.next 130 | if ['recommended', 'preserve', 'preserve-other'].include? custom_ownership 131 | found_ownership = true 132 | ohai "Setting pkgbuild option --ownership with value #{custom_ownership}" 133 | else 134 | opoo "#{custom_ownership} is not a valid value for pkgbuild --ownership option, ignoring" 135 | end 136 | end 137 | 138 | # Build it 139 | pkgfile = "#{name}-#{version}.pkg" 140 | ohai "Building package #{pkgfile}" 141 | args = [ 142 | "--quiet", 143 | "--root", "#{pkg_root}", 144 | "--identifier", identifier, 145 | "--version", version 146 | ] 147 | if found_scripts 148 | args << "--scripts" 149 | args << scripts_path 150 | end 151 | if found_ownership 152 | args << "--ownership" 153 | args << custom_ownership 154 | end 155 | args << "#{pkgfile}" 156 | safe_system "pkgbuild", *args 157 | 158 | FileUtils.rm_rf pkg_root 159 | end 160 | end 161 | 162 | Homebrew.pkg 163 | --------------------------------------------------------------------------------