Cross package maker. DEB/RPM generation or conversion. Derived from jordansissel/fpm.

⌈⌋ ⎇ branch:  cross package maker


Check-in [1f0b1d1b50]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:- some code cleanup - source rpm => target puppet works now
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 1f0b1d1b507861e7f11ec34da7194f6e4270e0b3
User & Date: jls@semicomplete.com 2011-06-21 06:00:18
Context
2011-06-21
06:04
- version bump; some serious internal shuffling happened to make puppet module generation a possibility. To that end, I bumped from 0.2.x to 0.3.x check-in: ef4e7851b2 user: jls@semicomplete.com tags: trunk
06:00
- some code cleanup - source rpm => target puppet works now check-in: 1f0b1d1b50 user: jls@semicomplete.com tags: trunk
02:33
- previous commit missed some files; adding features to allow generating multiple files from 'specfile' phase. check-in: d3f725a8ea user: jls@semicomplete.com tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to lib/fpm/builder.rb.

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
      @source.make_tarball!(tar_path, builddir)

      # Hack to unpack before generating the spec, etc.
      # Need to formalize this feature.
      # Perhaps something like @package.prepare
      if @package.respond_to?(:unpack_data_to)
        data_tarball = File.join(builddir, "data.tar.gz")
        FileUtils.mkdir_p(output)
        Dir.chdir(output) do
          FileUtils.mkdir_p(@package.unpack_data_to)
          system("gzip -d #{data_tarball}")
          Dir.chdir(@package.unpack_data_to) do
            @source.root = Dir.pwd
            system("tar -xf #{data_tarball.gsub(/\.gz$/, "")}")
          end
        end







<
|







87
88
89
90
91
92
93

94
95
96
97
98
99
100
101
      @source.make_tarball!(tar_path, builddir)

      # Hack to unpack before generating the spec, etc.
      # Need to formalize this feature.
      # Perhaps something like @package.prepare
      if @package.respond_to?(:unpack_data_to)
        data_tarball = File.join(builddir, "data.tar.gz")

        Dir.chdir(builddir) do
          FileUtils.mkdir_p(@package.unpack_data_to)
          system("gzip -d #{data_tarball}")
          Dir.chdir(@package.unpack_data_to) do
            @source.root = Dir.pwd
            system("tar -xf #{data_tarball.gsub(/\.gz$/, "")}")
          end
        end

Changes to lib/fpm/package.rb.

58
59
60
61
62
63
64

65
66
67
68
69
70
71

  # hash of paths for maintainer/package scripts (postinstall, etc)
  attr_accessor :scripts

  def initialize(source)
    @source = source
    @logger = Logger.new(STDERR)


    @name = source[:name] # || fail

    # Default version is 1.0 in case nobody told us a specific version.
    @version = source[:version] || "1.0"
    @epoch = source[:epoch]








>







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

  # hash of paths for maintainer/package scripts (postinstall, etc)
  attr_accessor :scripts

  def initialize(source)
    @source = source
    @logger = Logger.new(STDERR)
    @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN

    @name = source[:name] # || fail

    # Default version is 1.0 in case nobody told us a specific version.
    @version = source[:version] || "1.0"
    @epoch = source[:epoch]

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
  # TODO [Jay]: make this better...?
  def type
    self.class.name.split(':').last.downcase
  end # def type

  def template(path=nil)
    path ||= "#{type}.erb"
    @template ||= begin
      tpl = File.read("#{FPM::DIRS[:templates]}/#{path}")
      ERB.new(tpl, nil, "-")
    end
  end # def template

  def render_spec
    # find all files in paths given.
    paths = []
    @source.paths.each do |path|
      Find.find(path) { |p| paths << p }







|
|
|
<







116
117
118
119
120
121
122
123
124
125

126
127
128
129
130
131
132
  # TODO [Jay]: make this better...?
  def type
    self.class.name.split(':').last.downcase
  end # def type

  def template(path=nil)
    path ||= "#{type}.erb"
    @logger.info("Reading template: #{path}")
    tpl = File.read("#{FPM::DIRS[:templates]}/#{path}")
    return ERB.new(tpl, nil, "-")

  end # def template

  def render_spec
    # find all files in paths given.
    paths = []
    @source.paths.each do |path|
      Find.find(path) { |p| paths << p }
148
149
150
151
152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
      "#{name}-#{version}-#{iteration}.#{architecture}.#{type}"
    else
      "#{name}-#{version}.#{architecture}.#{type}"
    end
  end # def default_output

  def fixpath(path)
    p :fixpath => path
    if path.first != "/" 
      path = File.join(@source.root, path)
    end
    return path if File.symlink?(path)

    realpath = Pathname.new(path).realpath.to_s
    re = Regexp.new("^#{Regexp.escape(@source.root)}")
    realpath.gsub!(re, "")
    p :fixpath => {:path => realpath, :caller => caller[0] }
    return realpath
  end # def fixpath
end # class FPM::Package







<




>



|



148
149
150
151
152
153
154

155
156
157
158
159
160
161
162
163
164
165
166
      "#{name}-#{version}-#{iteration}.#{architecture}.#{type}"
    else
      "#{name}-#{version}.#{architecture}.#{type}"
    end
  end # def default_output

  def fixpath(path)

    if path.first != "/" 
      path = File.join(@source.root, path)
    end
    return path if File.symlink?(path)
    @logger.info(:fixpath => path)
    realpath = Pathname.new(path).realpath.to_s
    re = Regexp.new("^#{Regexp.escape(@source.root)}")
    realpath.gsub!(re, "")
    @logger.info(:fixpath_result => realpath)
    return realpath
  end # def fixpath
end # class FPM::Package

Changes to lib/fpm/source.rb.

22
23
24
25
26
27
28



29
30
31
32
33
34
35
    self[:dependencies] ||= []
  end

  attr_reader :paths
  attr_accessor :root

  def initialize(paths, root, params={})



    @paths = paths
    @root = root

    self[:suffix] = params[:suffix]
    self[:settings] = params[:settings]

    get_source(params)







>
>
>







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    self[:dependencies] ||= []
  end

  attr_reader :paths
  attr_accessor :root

  def initialize(paths, root, params={})
    @logger = Logger.new(STDERR)
    @logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN

    @paths = paths
    @root = root

    self[:suffix] = params[:suffix]
    self[:settings] = params[:settings]

    get_source(params)
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
    # TODO(sissel): To properly implement excludes as regexps, we
    # will need to find files ourselves. That may be more work
    # than it is worth. For now, rely on tar's --exclude.
    dir_tar = [tar_cmd, "--owner=root", "--group=root" ] \
              + excludes \
              + ["-cf", output, "--no-recursion" ] \
              + dirs
    p :dirs => dirs

    ::Dir.chdir(chdir) do
      system(*dir_tar) if dirs.any?
    end

    files_tar = [ tar_cmd ] \
                + excludes \







<







118
119
120
121
122
123
124

125
126
127
128
129
130
131
    # TODO(sissel): To properly implement excludes as regexps, we
    # will need to find files ourselves. That may be more work
    # than it is worth. For now, rely on tar's --exclude.
    dir_tar = [tar_cmd, "--owner=root", "--group=root" ] \
              + excludes \
              + ["-cf", output, "--no-recursion" ] \
              + dirs


    ::Dir.chdir(chdir) do
      system(*dir_tar) if dirs.any?
    end

    files_tar = [ tar_cmd ] \
                + excludes \

Changes to lib/fpm/source/rpm.rb.

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
require "fpm/source"

class FPM::Source::RPM < FPM::Source
  def get_metadata
    @rpm = @paths.first
    self[:name] = %x{rpm -q --qf '%{name}' -p #{@rpm}}

    self[:version] = %x{rpm -q --qf '%{version}' -p #{@rpm}}
    self[:iteration] = %x{rpm -q --qf '%{release}' -p #{@rpm}}
    self[:summary] = %x{rpm -q --qf '%{summary}' -p #{@rpm}}
    #self[:description] = %x{rpm -q --qf '%{description}' -p #{@rpm}}
    self[:dependencies] = %x{rpm -qRp #{@rpm}}.split("\n")\
      .collect { |line| line.strip }

    @paths = %x{rpm -qlp #{@rpm}}.split("\n")
  end

  def make_tarball!(tar_path, builddir)
    tmpdir = "#{tar_path}.dir"
    ::Dir.mkdir(tmpdir)
    system("rpm2cpio #{@rpm} | (cd #{tmpdir}; cpio -i --make-directories)")
    tar(tar_path, ".", tmpdir)
    @paths = "."
    @root = tmpdir

    # TODO(sissel): Make a helper method.
    system(*["gzip", "-f", tar_path])
  end
end





|

|
|
|












|
<
<




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
require "fpm/source"

class FPM::Source::RPM < FPM::Source
  def get_metadata
    @rpm = @paths.first
    self[:name] = %x{rpm -q --qf '%{name}' -p #{@rpm}}.chomp

    self[:version] = %x{rpm -q --qf '%{version}' -p #{@rpm}}.chomp
    self[:iteration] = %x{rpm -q --qf '%{release}' -p #{@rpm}}.chomp
    self[:summary] = %x{rpm -q --qf '%{summary}' -p #{@rpm}}.chomp
    #self[:description] = %x{rpm -q --qf '%{description}' -p #{@rpm}}
    self[:dependencies] = %x{rpm -qRp #{@rpm}}.split("\n")\
      .collect { |line| line.strip }

    @paths = %x{rpm -qlp #{@rpm}}.split("\n")
  end

  def make_tarball!(tar_path, builddir)
    tmpdir = "#{tar_path}.dir"
    ::Dir.mkdir(tmpdir)
    system("rpm2cpio #{@rpm} | (cd #{tmpdir}; cpio -i --make-directories)")
    tar(tar_path, ".", tmpdir)
    @paths = ["."]


    # TODO(sissel): Make a helper method.
    system(*["gzip", "-f", tar_path])
  end
end

Changes to lib/fpm/target/deb.rb.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

  def architecture
    if @architecture.nil? or @architecture == "native"
      # Default architecture should be 'native' which we'll need
      # to ask the system about.
      arch = %x{dpkg --print-architecture}.chomp
      if $?.exitstatus != 0
        arch = %x{uname -m}
        @logger.warn("Can't find 'dpkg' tool (need it to get default " \
                     "architecture!). Please specificy --architecture " \
                     "specifically. (Defaulting now to #{arch})")
      end
      @architecture = arch
    elsif @architecture == "x86_64"
      # Debian calls x86_64 "amd64"







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

  def architecture
    if @architecture.nil? or @architecture == "native"
      # Default architecture should be 'native' which we'll need
      # to ask the system about.
      arch = %x{dpkg --print-architecture}.chomp
      if $?.exitstatus != 0
        arch = %x{uname -m}.chomp
        @logger.warn("Can't find 'dpkg' tool (need it to get default " \
                     "architecture!). Please specificy --architecture " \
                     "specifically. (Defaulting now to #{arch})")
      end
      @architecture = arch
    elsif @architecture == "x86_64"
      # Debian calls x86_64 "amd64"

Changes to lib/fpm/target/puppet.rb.

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
require "erb"
require "fpm/namespace"
require "fpm/package"
require "fpm/errors"
require "etc"


class ::Dir
  class << self
    alias :orig_mkdir :mkdir

    def mkdir(*args)
      p :mkdir => { :args => args, :caller => caller }
      orig_mkdir(*args)
    end
  end
end

require "fileutils" # for FileUtils

# TODO(sissel): Add dependency checking support.
# IIRC this has to be done as a 'checkinstall' step.
class FPM::Target::Puppet < FPM::Package
  def architecture





>

|
|
|
|
|
|
|
|
|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
require "erb"
require "fpm/namespace"
require "fpm/package"
require "fpm/errors"
require "etc"
require "ftools"

#class ::Dir
  #class << self
    #alias :orig_mkdir :mkdir
#
    #def mkdir(*args)
      ##p :mkdir => { :args => args, :caller => caller }
      #orig_mkdir(*args)
    #end
  #end
#end

require "fileutils" # for FileUtils

# TODO(sissel): Add dependency checking support.
# IIRC this has to be done as a 'checkinstall' step.
class FPM::Target::Puppet < FPM::Package
  def architecture
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
    "#{builddir}/package.pp"
  end # def specfile
  
  # Default specfile generator just makes one specfile, whatever that is for
  # this package.
  def generate_specfile(builddir)
    paths = []



    @source.paths.each do |path|

      Find.find(path) { |p| paths << p }
    end


    manifests = %w{package.pp package/remove.pp}


    manifests.each do |name|
      dir = File.join(builddir, File.dirname(name))
      @logger.info("manifests targeting: #{dir}")
      Dir.mkdir(dir) if !File.directory?(dir)
      
      @logger.info("Generating #{name}")
      File.open(File.join(builddir, name), "w") do |f|

        f.puts template(File.join("puppet", "#{name}.erb")).result(binding)



      end
    end
  end # def generate_specfile

  # Override render_spec so we can generate multiple files for puppet.
  # The package.pp, package/remove.pp, 
  def render_spec
    # find all files in paths given.
    paths = []
    @source.paths.each do |path|
      Find.find(path) { |p| paths << p }
    end
    #@logger.info(:paths => paths.sort)
    template.result(binding)
  end # def render_spec

  def unpack_data_to
    "files"
  end

  def build!(params)
    # TODO(sissel): Support these somehow, perhaps with execs and files.
    self.scripts.each do |name, path|







>
>
>
|
>
|
|
>
>

>
>
|
|

|

<
|
>
|
>
>
>















>







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
    "#{builddir}/package.pp"
  end # def specfile
  
  # Default specfile generator just makes one specfile, whatever that is for
  # this package.
  def generate_specfile(builddir)
    paths = []
    @logger.info("PWD: #{File.join(builddir, unpack_data_to)}")
    fileroot = File.join(builddir, unpack_data_to)
    Dir.chdir(fileroot) do
      Find.find(".") do |p|
        next if p == "."
        paths << p
      end
    end
    @logger.info(paths[-1])
    manifests = %w{package.pp package/remove.pp}

    ::Dir.mkdir(File.join(builddir, "manifests"))
    manifests.each do |manifest|
      dir = File.join(builddir, "manifests", File.dirname(manifest))
      @logger.info("manifests targeting: #{dir}")
      ::Dir.mkdir(dir) if !File.directory?(dir)
      

      File.open(File.join(builddir, "manifests", manifest), "w") do |f|
        @logger.info("manifest: #{f.path}")
        template = template(File.join("puppet", "#{manifest}.erb"))
        ::Dir.chdir(fileroot) do
          f.puts template.result(binding)
        end
      end
    end
  end # def generate_specfile

  # Override render_spec so we can generate multiple files for puppet.
  # The package.pp, package/remove.pp, 
  def render_spec
    # find all files in paths given.
    paths = []
    @source.paths.each do |path|
      Find.find(path) { |p| paths << p }
    end
    #@logger.info(:paths => paths.sort)
    template.result(binding)
  end # def render_spec

  def unpack_data_to
    "files"
  end

  def build!(params)
    # TODO(sissel): Support these somehow, perhaps with execs and files.
    self.scripts.each do |name, path|
80
81
82
83
84
85
86
87
88
89
90
91

92



93
94
95

96
97
98
99
100
101
102

    if File.exists?(params[:output])
      # TODO(sissel): Allow folks to choose output?
      @logger.error("Puppet module directory '#{params[:output]}' already " \
                    "exists. Delete it or choose another output (-p flag)")
    end

    Dir.mkdir(params[:output])
    builddir = Dir.pwd

    Dir.chdir(params[:output]) do
      Dir.mkdir("manifests")

      FileUtils.cp(specfile(builddir), "manifests")



    end
    # Files are now in the 'files' path
    # Generate a manifest 'package.pp' with all the information from

  end # def build!

  # The directory we create should just be the name of the package as the
  # module name
  def default_output
    name
  end # def default_output







|
|

|
|
>
|
>
>
>
|
<
<
>







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

    if File.exists?(params[:output])
      # TODO(sissel): Allow folks to choose output?
      @logger.error("Puppet module directory '#{params[:output]}' already " \
                    "exists. Delete it or choose another output (-p flag)")
    end

    ::Dir.mkdir(params[:output])
    builddir = ::Dir.pwd

    # Copy 'files' from builddir to :output/files
    Find.find("files", "manifests") do |path|
      @logger.info("Copying path: #{path}")
      if File.directory?(path)
        ::Dir.mkdir(File.join(params[:output], path))
      else
        File.copy(path, File.join(params[:output], path))
      end


    end
  end # def build!

  # The directory we create should just be the name of the package as the
  # module name
  def default_output
    name
  end # def default_output

Changes to templates/puppet/package/remove.pp.erb.

1
2
3
4


5
6
7
8
9
10
11
class <%= name %>::package::remove {
  $version = "<%= version %>"
  $iteration = "<%= iteration %>"



  file {
<% paths.each do |path| -%>
    "<%= fixpath(path) %>":
      <%= settings %>;
<% end # paths.each -%>
  }
}




>
>



|



1
2
3
4
5
6
7
8
9
10
11
12
13
class <%= name %>::package::remove {
  $version = "<%= version %>"
  $iteration = "<%= iteration %>"

  fail("DO NOT USE THIS. IT IS NOT WELL DEFINED YET.")

  file {
<% paths.each do |path| -%>
    "<%= fixpath(path) %>":
      ensure => absent;
<% end # paths.each -%>
  }
}