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

⌈⌋ ⎇ branch:  cross package maker


Check-in [9f16bb5dd3]

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

Overview
Comment:- Fix gem-ification bits (now installs+runs) - Factor out 'deb' package assembly into a library
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 9f16bb5dd3fab936435708eb96a1ba360a3e60b9
User & Date: jls@semicomplete.com 2011-01-04 02:22:09
Context
2011-01-04
02:47
- Move to markdown check-in: ef0f1b9dc7 user: jls@semicomplete.com tags: trunk
02:22
- Fix gem-ification bits (now installs+runs) - Factor out 'deb' package assembly into a library check-in: 9f16bb5dd3 user: jls@semicomplete.com tags: trunk
01:25
- Add gem packaging check-in: 9111aeaef0 user: jls@semicomplete.com tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to bin/fpm.

1
2
3

4
5
6

7
8
9
10
11
12
13

14
15
16
17
18
19
20
#!/usr/bin/env ruby
#


require "optparse"
require "ostruct"
require "erb"


def main(args)
  settings = OpenStruct.new

  opts = OptionParser.new do |opts|
    opts.banner = "Usage: #{$0} [options]"
    

    opts.on("-p PACKAGEFILE", "--package PACKAGEFILE",
            "The package file to manage") do |path|
      if path =~ /^\//
        settings.package_path = path
      else
        settings.package_path = "#{Dir.pwd}/#{path}"
      end



>



>







>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env ruby
#

require "rubygems"
require "optparse"
require "ostruct"
require "erb"
require "fpm"

def main(args)
  settings = OpenStruct.new

  opts = OptionParser.new do |opts|
    opts.banner = "Usage: #{$0} [options]"
    
    # TODO(sissel): Maybe this should be '-o OUTPUT' ?
    opts.on("-p PACKAGEFILE", "--package PACKAGEFILE",
            "The package file to manage") do |path|
      if path =~ /^\//
        settings.package_path = path
      else
        settings.package_path = "#{Dir.pwd}/#{path}"
      end
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
    opts.on("-C DIRECTORY", "Change directory before searching for files") do |dir|
      settings.chdir = dir
    end
  end # OptionParser

  opts.parse!(args)

  # Actions:
  # create package
  #
  # files: add, remove
  # scripts: add, remove
  # metadata: set, add, remove

  if !settings.package_path
    $stderr.puts "No package file given to manage. Give with -p PACKAGEFILE"
    return 1
  end

  paths = args
  type = settings.package_path.split(".")[-1]

  mkbinding = lambda do
    package = settings.package_name
    version = settings.version
    package_iteration = 1

    maintainer = (settings.maintainer or "<#{ENV["USER"]}>")
    category = (settings.category or "X11")
    summary = (settings.summary or "no summary given for #{package}-#{version}")

    architecture = (settings.architecture or %x(uname -m).chomp)
    if architecture == "x86_64" && type == "deb"
      architecture = "amd64"
    end


    url = (settings.url or "http://example.com/")
    dependencies = (settings.dependencies or [])
    return binding
  end

  metadata = mkbinding.call

  package_path = settings.package_path
  package_path.gsub!(/VERSION/, eval('"#{version}-#{package_iteration}"', metadata))
  package_path.gsub!(/ARCH/, eval("architecture", metadata))


  if type != "deb"
    $stderr.puts "Unsupported package type '#{type}'"
    return 1
  end


  builddir = "#{Dir.pwd}/build-#{type}-#{File.basename(package_path)}"
  Dir.mkdir(builddir) if !File.directory?(builddir)
  template = File.new("#{File.dirname(__FILE__)}/../templates/#{type}.erb").read()

  Dir.chdir(settings.chdir || ".") do 
    puts Dir.pwd

    # Add directories first.
    dirs = []
    paths.each do |path|
      while path != "/" and path != "."
        if !dirs.include?(path) or File.symlink?(path)
          dirs << path 
        else
          #puts "Skipping: #{path}"
          #puts !dirs.include?(path) 
          #puts !File.symlink?(path)
        end
        path = File.dirname(path)
      end
    end
    dirs = dirs.sort { |a,b| a.length <=> b.length}
    puts dirs.join("\n")
    system(*["tar", "--owner=root", "--group=root", "-cf", "#{builddir}/data.tar", "--no-recursion", *dirs])
    puts paths.join("\n")
    #paths = paths.reject { |p| File.symlink?(p) }
    system(*["tar", "--owner=root", "--group=root", "-rf", "#{builddir}/data.tar", *paths])
    system(*["gzip", "-f", "#{builddir}/data.tar"])

    # Generate md5sums
    md5sums = []
    paths.each do |path|
      md5sums += %x{find #{path} -type f -print0 | xargs -0 md5sum}.split("\n")
    end
    File.open("#{builddir}/md5sums", "w") { |f| f.puts md5sums.join("\n") }

    # Generate 'control' file
    control = ERB.new(template).result(metadata)
    File.open("#{builddir}/control", "w") { |f| f.puts control }
  end

  # create control.tar.gz
  Dir.chdir(builddir) do
    system("tar -zcf control.tar.gz control md5sums")
  end

  Dir.chdir(builddir) do
    # create debian-binary
    File.open("debian-binary", "w") { |f| f.puts "2.0" }
  end

  Dir.chdir(builddir) do
    system("ar -qc #{package_path} debian-binary control.tar.gz data.tar.gz")
  end
end

ret = main(ARGV) 
exit(ret != nil ? ret : 0)







<
<
<
<
<
<
<





<
<
|
<
|
|
<
<
|
<
<
<
|
<
<
<
|
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



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
    opts.on("-C DIRECTORY", "Change directory before searching for files") do |dir|
      settings.chdir = dir
    end
  end # OptionParser

  opts.parse!(args)








  if !settings.package_path
    $stderr.puts "No package file given to manage. Give with -p PACKAGEFILE"
    return 1
  end



  package = FPM::Deb.new

  package.name = settings.package_name
  package.version = settings.version


  package.maintainer = settings.maintainer if settings.maintainer



  package.architecture = settings.architecture or %x{uname -m}.chomp



  package.assemble({








    "output" => settings.package_path,














    "root" => settings.chdir,

    "paths" => args, # Remainder of args are paths.





















  })




end # def main






















ret = main(ARGV) 
exit(ret != nil ? ret : 0)

Changes to fpm.gemspec.

1
2
3
4
5
6
7
8
9
10
Gem::Specification.new do |spec|
  files = []
  dirs = %w{lib bin}
  dirs.each do |dir|
    files += Dir["#{dir}/**/*"]
  end

  rev = Time.now.strftime("%Y%m%d%H%M%S")
  spec.name = "fpm"
  spec.version = "0.1.0"


|







1
2
3
4
5
6
7
8
9
10
Gem::Specification.new do |spec|
  files = []
  dirs = %w{lib bin templates}
  dirs.each do |dir|
    files += Dir["#{dir}/**/*"]
  end

  rev = Time.now.strftime("%Y%m%d%H%M%S")
  spec.name = "fpm"
  spec.version = "0.1.0"

Added lib/fpm.rb.







>
>
>
1
2
3
require "fpm/namespace"
require "fpm/package"
require "fpm/deb"

Added lib/fpm/deb.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
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
#!/usr/bin/env ruby
#

require "erb"
require "fpm/namespace"
require "fpm/package"

class FPM::Deb < FPM::Package

  # Assemble the package.
  # params:
  #  "root" => "/some/path"   # the 'root' of your package directory
  #  "paths" => [ "/some/path" ...]  # paths to icnlude in this package
  #  "output" => "foo.deb"  # what to output to.
  #
  # The 'output' file path will have 'VERSION' and 'ARCH' replaced with
  # the appropriate values if if you want the filename generated.
  def assemble(params)
    raise "No package name given. Can't assemble package" if !@name

    root = params["root"]
    paths = params["paths"]
    output = params["output"]
    type = "deb" 

    p self.inspect

    # Debian calls x86_64 "amd64"
    @architecture = "amd64" if @architecture == "x86_64"

    output.gsub!(/VERSION/, "#{@version}-#{@iteration}")
    output.gsub!(/ARCH/, @architecture)

    builddir = "#{Dir.pwd}/build-#{type}-#{File.basename(output)}"
    @garbage << builddir

    Dir.mkdir(builddir) if !File.directory?(builddir)

    Dir.chdir(root || ".") do 
      self.tar("#{builddir}/data.tar", paths)
      # TODO(sissel): Make a helper method.
      system(*["gzip", "-f", "#{builddir}/data.tar"])

      # Generate md5sums
      md5sums = self.checksum(paths)
      File.open("#{builddir}/md5sums", "w") { |f| f.puts md5sums.join("\n") }

      # Generate 'control' file
      template = File.new("#{File.dirname(__FILE__)}/../../templates/deb.erb").read()
      control = ERB.new(template).result(binding)
      File.open("#{builddir}/control", "w") { |f| f.puts control }
    end

    # create control.tar.gz
    Dir.chdir(builddir) do
      # Make the control
      system("tar -zcf control.tar.gz control md5sums")
     
      # create debian-binary
      File.open("debian-binary", "w") { |f| f.puts "2.0" }

      # pack up the .deb
      system("ar -qc #{output} debian-binary control.tar.gz data.tar.gz")
    end
  end  # def assemble

  def checksum(paths)
    md5sums = []
    paths.each do |path|
      md5sums += %x{find #{path} -type f -print0 | xargs -0 md5sum}.split("\n")
    end
  end # def checksum
end # class FPM::Deb

Added lib/fpm/namespace.rb.



>
1
module FPM; end

Added lib/fpm/package.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
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
require "fpm/namespace"
require "socket" # for Socket.gethostname

class FPM::Package 
  # The name of this package
  attr_accessor :name

  # The version of this package (the upstream version)
  attr_accessor :version

  # The iteration of this package.
  #   Debian calls this 'release' and is the last '-NUMBER' in the version
  #   RedHat has this as 'Release' in the .spec file
  #   FreeBSD calls this 'PORTREVISION' 
  # If left unpicked, it defaults to 1.
  attr_accessor :iteration

  # Who maintains this package? This could be the upstream author
  # or the package maintainer. You pick.
  attr_accessor :maintainer

  # URL for this package.
  # Could be the homepage. Could be the download url. You pick.
  attr_accessor :url

  # The category of this package.
  # RedHat calls this 'Group'
  # Debian calls this 'Section'
  # FreeBSD would put this in /usr/ports/<category>/...
  attr_accessor :category

  # A identifier representing the license. Any string is fine.
  attr_accessor :license

  # What architecture is this package for?
  attr_accessor :architecture

  # TODO(sissel): Dependencies?
  
  def initialize
    @iteration = 1
    @url = ""
    @category = "default"
    @license = "unknown"
    @maintainer = "<#{ENV["USER"]}@#{Socket.gethostname}>"
    @architecture = nil

    # Garbage is stuff you may want to clean up.
    @garbage = []
  end

  def tar(output, paths)
    dirs = []
    paths.each do |path|
      while path != "/" and path != "."
        dirs << path if !dirs.include?(path) 
        path = File.dirname(path)
      end
    end # paths.each
    dirs = dirs.sort { |a,b| a.length <=> b.length}
    system(*["tar", "--owner=root", "--group=root", "-cf", output, "--no-recursion", *dirs])
    system(*["tar", "--owner=root", "--group=root", "-rf", output, *paths])
  end

end

Changes to templates/deb.erb.

1
2
3
4
5
6
7
8
9
10
11
12
Package: <%= package %>
Version: <%= version %>-<%= package_iteration %>
Architecture: <%= architecture %>
Maintainer: <%= maintainer %>
Installed-Size: 12345
Depends: <%= dependencies.join(", ") %>
Section: <%= category %> 
Priority: extra
Homepage: <%= url %>
Description: <%= summary %>
  <%= summary %>

|
|
|
|

|
|

|
|
|

1
2
3
4
5
6
7
8
9
10
11
12
Package: <%= @package %>
Version: <%= @version %>-<%= @package_iteration %>
Architecture: <%= @architecture %>
Maintainer: <%= @maintainer %>
Installed-Size: 12345
Depends: <%= @dependencies.join(", ") rescue "" %>
Section: <%= @category %> 
Priority: extra
Homepage: <%= @url %>
Description: <%= @summary %>
  <%= @summary %>

Changes to tools/npm2pkg.rb.

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
    maintainer = package["author"]
  else
    m = package["maintainers"][0] \
      rescue { "name" => "missing upstream author", "email" => ENV["USER"] }
    maintainer = "#{m["name"]} <#{m["email"]}>"
  end

  pkgcmd = [ "ruby", "bin/pkg.rb", 
    "-n", "nodejs-#{package["name"]}",
    "-v", package["version"],
    "-m", maintainer,
    "-a", "all",
  ]

  depends.each do |dep|







|







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
    maintainer = package["author"]
  else
    m = package["maintainers"][0] \
      rescue { "name" => "missing upstream author", "email" => ENV["USER"] }
    maintainer = "#{m["name"]} <#{m["email"]}>"
  end

  pkgcmd = [ "fpm", 
    "-n", "nodejs-#{package["name"]}",
    "-v", package["version"],
    "-m", maintainer,
    "-a", "all",
  ]

  depends.each do |dep|