fpm 0.4.32 → 0.4.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELIST CHANGED
@@ -1,3 +1,19 @@
1
+ 0.4.35 (May 8, 2013)
2
+ - Fix dependencies listed in the gem.
3
+
4
+ 0.4.34 (May 7, 2013)
5
+ - Now supports CPAN - Perl mongers rejoice! For example:
6
+ 'fpm -s cpan -t deb DBI'
7
+ - deb: fixed some additional complaints by lintian (#420, patch by Pranay
8
+ Kanwar)
9
+ - rpm: add flags --rpm-autoreqprov, --rpm-autoreq, and --rpm-autoprov
10
+ to tell rpm to enable that feature in the rpm spec. (#416, patch by Adam
11
+ Stephens)
12
+
13
+ 0.4.33 (April 9, 2013)
14
+ - Now supports npm, the node package manager. For example:
15
+ 'fpm -s npm -t deb express'
16
+
1
17
  0.4.32 (April 9, 2013)
2
18
  - COMPATIBILITY WARNING: rpm: The default epoch is now nothing because this
3
19
  aligns more closely with typical rpm packages in the real world. This
@@ -18,6 +18,7 @@ Thomas Meson (github: zllak)
18
18
  Oliver Hookins (github: ohookins)
19
19
  llasram
20
20
  sbuss
21
+ Brett Gailey (github: dnbert)
21
22
 
22
23
  If you have contributed (bug reports, feature requests, help in IRC, blog
23
24
  posts, code, etc) and aren't listed here, please let me know if you wish to be
@@ -77,52 +77,34 @@ class FPM::Command < Clamp::Command
77
77
  option ["-d", "--depends"], "DEPENDENCY",
78
78
  "A dependency. This flag can be specified multiple times. Value is " \
79
79
  "usually in the form of: -d 'name' or -d 'name > version'",
80
- :default => [], :attribute_name => :dependencies do |val|
81
- # Clamp doesn't support multivalue flags (ie; specifying -d multiple times)
82
- # so we can hack around it with this trickery.
83
- @dependencies ||= []
84
- @dependencies << val
85
- end # -d / --depends
80
+ :multivalued => true, :attribute_name => :dependencies
86
81
 
87
82
  option "--no-depends", :flag, "Do not list any dependencies in this package",
88
83
  :default => false
89
84
 
90
- option "--no-auto-depends", :flag, "Do not list any dependencies in this package automatically",
91
- :default => false
85
+ option "--no-auto-depends", :flag, "Do not list any dependencies in this" \
86
+ "package automatically", :default => false
92
87
 
93
88
  option "--provides", "PROVIDES",
94
89
  "What this package provides (usually a name). This flag can be "\
95
- "specified multiple times." do |val|
96
- @provides ||= []
97
- @provides << val
98
- end # --provides
90
+ "specified multiple times.", :multivalued => true,
91
+ :attribute_name => :provides
99
92
  option "--conflicts", "CONFLICTS",
100
93
  "Other packages/versions this package conflicts with. This flag can " \
101
- "specified multiple times." do |val|
102
- @conflicts ||= []
103
- @conflicts << val
104
- end # --conflicts
94
+ "specified multiple times.", :multivalued => true,
95
+ :attribute_name => :conflicts
105
96
  option "--replaces", "REPLACES",
106
97
  "Other packages/versions this package replaces. This flag can be "\
107
- "specified multiple times." do |val|
108
- @replaces ||= []
109
- @replaces << val
110
- end # --replaces
98
+ "specified multiple times.", :multivalued => true,
99
+ :attribute_name => :replaces
100
+
111
101
  option "--config-files", "CONFIG_FILES",
112
102
  "Mark a file in the package as being a config file. This uses 'conffiles'" \
113
103
  " in debs and %config in rpm. If you have multiple files to mark as " \
114
- "configuration files, specify this flag multiple times." do |val|
115
- #You can specify a directory to have it scanned marking all files found as
116
- #config files. If you have multiple "
117
- @config_files ||= []
118
- @config_files << val
119
- end # --config-files
120
- option "--directories", "DIRECTORIES",
121
- "Mark a directory as being owned by the package" \
122
- do |val|
123
- @directories ||= []
124
- @directories << val
125
- end # directories
104
+ "configuration files, specify this flag multiple times.",
105
+ :multivalued => true, :attribute_name => :config_files
106
+ option "--directories", "DIRECTORIES", "Mark a directory as being owned " \
107
+ "by the package", :multivalued => true, :attribute_name => :directories
126
108
  option ["-a", "--architecture"], "ARCHITECTURE",
127
109
  "The architecture name. Usually matches 'uname -m'. For automatic values," \
128
110
  " you can use '-a all' or '-a native'. These two strings will be " \
@@ -134,12 +116,14 @@ class FPM::Command < Clamp::Command
134
116
  "a name suffix to append to package and dependencies."
135
117
  option ["-e", "--edit"], :flag,
136
118
  "Edit the package spec before building.", :default => false
119
+
120
+ excludes = []
137
121
  option ["-x", "--exclude"], "EXCLUDE_PATTERN",
138
122
  "Exclude paths matching pattern (shell wildcard globs valid here). " \
139
123
  "If you have multiple file patterns to exclude, specify this flag " \
140
124
  "multiple times.", :attribute_name => :excludes do |val|
141
- @excludes ||= []
142
- @excludes << val
125
+ excludes << val
126
+ next excludes
143
127
  end # -x / --exclude
144
128
  option "--description", "DESCRIPTION", "Add a description for this package." \
145
129
  " You can include '\n' sequences to indicate newline breaks.",
@@ -199,10 +183,10 @@ class FPM::Command < Clamp::Command
199
183
 
200
184
  option "--template-value", "KEY=VALUE",
201
185
  "Make 'key' available in script templates, so <%= key %> given will be " \
202
- "the provided value. Implies --template-scripts" do |kv|
186
+ "the provided value. Implies --template-scripts",
187
+ :multivalued => true do |kv|
203
188
  @template_scripts = true
204
- @template_values ||= []
205
- @template_values << kv.split("=", 2)
189
+ next kv.split("=", 2)
206
190
  end
207
191
 
208
192
  option "--workdir", "WORKDIR",
@@ -220,16 +204,6 @@ class FPM::Command < Clamp::Command
220
204
  klass.apply_options(self)
221
205
  end
222
206
 
223
- # TODO(sissel): expose 'option' and 'parameter' junk to FPM::Package and subclasses.
224
- # Apply those things to this command.
225
- #
226
- # Add extra flags from plugins
227
- #FPM::Package::Gem.flags(FPM::Flags.new(opts, "gem", "gem only"), @settings)
228
- #FPM::Package::Python.flags(FPM::Flags.new(opts, "python", "python only"),
229
- #@settings)
230
- #FPM::Package::Deb.flags(FPM::Flags.new(opts, "deb", "deb only"), @settings)
231
- #FPM::Package::Rpm.flags(FPM::Flags.new(opts, "rpm", "rpm only"), @settings)
232
-
233
207
  # A new FPM::Command
234
208
  def initialize(*args)
235
209
  super(*args)
@@ -239,7 +213,6 @@ class FPM::Command < Clamp::Command
239
213
  @dependencies = []
240
214
  @config_files = []
241
215
  @directories = []
242
- @excludes = []
243
216
  end # def initialize
244
217
 
245
218
  # Execute this command. See Clamp::Command#execute and Clamp's documentation
@@ -406,8 +379,8 @@ class FPM::Command < Clamp::Command
406
379
  output = input.convert(output_class)
407
380
 
408
381
  # Provide any template values as methods on the package.
409
- if !@template_values.nil?
410
- @template_values.each do |key, value|
382
+ if template_scripts?
383
+ template_value_list.each do |key, value|
411
384
  (class << output; self; end).send(:define_method, key) { value }
412
385
  end
413
386
  end
@@ -93,9 +93,6 @@ class FPM::Package
93
93
  # (Not all packages support this)
94
94
  attr_accessor :replaces
95
95
 
96
- # Array of glob patterns to exclude from this package
97
- attr_accessor :excludes
98
-
99
96
  # a summary or description of the package
100
97
  attr_accessor :description
101
98
 
@@ -315,8 +312,11 @@ class FPM::Package
315
312
  .collect { |path| path[staging_path.length + 1.. -1] }
316
313
  end # def files
317
314
 
315
+ def template_dir
316
+ File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "templates"))
317
+ end
318
+
318
319
  def template(path)
319
- template_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "templates"))
320
320
  template_path = File.join(template_dir, path)
321
321
  template_code = File.read(template_path)
322
322
  @logger.info("Reading template", :path => template_path)
@@ -0,0 +1,190 @@
1
+ require "fpm/namespace"
2
+ require "fpm/package"
3
+ require "fpm/util"
4
+ require "fileutils"
5
+ require "find"
6
+
7
+ class FPM::Package::CPAN < FPM::Package
8
+ # Flags '--foo' will be accessable as attributes[:npm_foo]
9
+ option "--perl-bin", "PERL_EXECUTABLE",
10
+ "The path to the perl executable you wish to run.", :default => "perl"
11
+ option "--cpanm-bin", "CPANM_EXECUTABLE",
12
+ "The path to the cpanm executable you wish to run.", :default => "cpanm"
13
+ option "--package-name-prefix", "NAME_PREFIX", "Name to prefix the package " \
14
+ "name with.", :default => "perl"
15
+ option "--test", :flag, "Run the tests before packaging?", :default => true
16
+
17
+ private
18
+ def input(package)
19
+ require "ftw" # for http access
20
+ require "json"
21
+
22
+ agent = FTW::Agent.new
23
+ result = search(package, agent)
24
+ tarball = download(result, agent)
25
+ moduledir = unpack(tarball)
26
+
27
+ # Read package metadata (name, version, etc)
28
+ if File.exists?(File.join(moduledir, "META.json"))
29
+ metadata = JSON.parse(File.read(File.join(moduledir, ("META.json"))))
30
+ elsif File.exists?(File.join(moduledir, ("META.yml")))
31
+ require "yaml"
32
+ metadata = YAML.load_file(File.join(moduledir, ("META.yml")))
33
+ else
34
+ raise FPM::InvalidPackageConfiguration,
35
+ "Could not find package metadata. Checked for META.json and META.yml"
36
+ end
37
+ self.version = metadata["version"]
38
+ self.description = metadata["abstract"]
39
+
40
+ self.license = case metadata["license"]
41
+ when Array; metadata["license"].first
42
+ else; metadata["license"]
43
+ end
44
+
45
+ if metadata.include?("distribution")
46
+ @logger.info("Setting package name from 'distribution'",
47
+ :distribution => metadata["distribution"])
48
+ self.name = fix_name(metadata["distribution"])
49
+ else
50
+ @logger.info("Setting package name from 'name'",
51
+ :name => metadata["name"])
52
+ self.name = fix_name(metadata["name"])
53
+ end
54
+
55
+ # Not all things have 'author' listed.
56
+ self.vendor = metadata["author"].join(", ") if metadata.include?("author")
57
+ self.url = metadata["resources"]["homepage"] rescue "unknown"
58
+
59
+ # TODO(sissel): figure out if this perl module compiles anything
60
+ # and set the architecture appropriately.
61
+ self.architecture = "all"
62
+
63
+ # Install any build/configure dependencies with cpanm.
64
+ # We'll install to a temporary directory.
65
+ @logger.info("Installing any build or configure dependencies")
66
+ safesystem(attributes[:cpan_cpanm_bin], "-L", build_path("cpan"), moduledir)
67
+
68
+ if !attributes[:no_auto_depends?]
69
+ if metadata.include?("requires")
70
+ metadata["requires"].each do |dep_name, version|
71
+ # Special case for representing perl core as a version.
72
+ if dep_name == "perl"
73
+ self.dependencies << "#{dep_name} >= #{version}"
74
+ next
75
+ end
76
+ dep = search(dep_name, agent)
77
+
78
+ if dep.include?("distribution")
79
+ name = fix_name(dep["distribution"])
80
+ else
81
+ name = fix_name(dep_name)
82
+ end
83
+
84
+ if version.to_s == "0"
85
+ # Assume 'Foo = 0' means any version?
86
+ self.dependencies << "#{name}"
87
+ else
88
+ self.dependencies << "#{name} = #{version}"
89
+ end
90
+ end
91
+ end
92
+ end #no_auto_depends
93
+
94
+ ::Dir.chdir(moduledir) do
95
+ # TODO(sissel): install build and config dependencies to resolve
96
+ # build/configure requirements.
97
+ # META.yml calls it 'configure_requires' and 'build_requires'
98
+ # META.json calls it prereqs/build and prereqs/configure
99
+
100
+ prefix = attributes[:prefix] || "/usr/local"
101
+ # TODO(sissel): Set default INSTALL path?
102
+ # perl -e 'use Config; print "$Config{sitelib}"'
103
+ safesystem(attributes[:cpan_perl_bin],
104
+ "-Mlocal::lib=#{build_path("cpan")}",
105
+ "Makefile.PL",
106
+ "PREFIX=#{prefix}",
107
+ # Have to specify INSTALL_BASE as empty otherwise
108
+ # Makefile.PL lies and claims we've set both PREFIX and
109
+ # INSTALL_BASE.
110
+ "INSTALL_BASE="
111
+ )
112
+
113
+ make = [ "make" ]
114
+
115
+ safesystem(*make)
116
+ safesystem(*(make + ["test"])) if attributes[:cpan_test?]
117
+ safesystem(*(make + ["DESTDIR=#{staging_path}", "install"]))
118
+ end
119
+
120
+ # TODO(sissel): figure out if this perl module compiles anything
121
+ # and set the architecture appropriately.
122
+ self.architecture = "all"
123
+
124
+ # Find any shared objects in the staging directory to set architecture as
125
+ # native if found; otherwise keep the 'all' default.
126
+ Find.find(staging_path) do |path|
127
+ if path =~ /\.so$/
128
+ @logger.info("Found shared library, setting architecture=native",
129
+ :path => path)
130
+ self.architecture = "native"
131
+ end
132
+ end
133
+ end
134
+
135
+ def unpack(tarball)
136
+ directory = build_path("module")
137
+ ::Dir.mkdir(directory)
138
+ args = [ "-C", directory, "-zxf", tarball,
139
+ "--strip-components", "1" ]
140
+ safesystem("tar", *args)
141
+ return directory
142
+ end
143
+
144
+ def download(metadata, agent)
145
+ distribution = metadata["distribution"]
146
+ author = metadata["author"]
147
+ @logger.info("Downloading perl module",
148
+ :distribution => distribution,
149
+ :version => version)
150
+
151
+ # default to latest versionunless we specify one
152
+ version = metadata["version"] if version.nil?
153
+
154
+ tarball = "#{distribution}-#{version}.tar.gz"
155
+ url = "http://search.cpan.org/CPAN/authors/id/#{author[0,1]}/#{author[0,2]}/#{author}/#{tarball}"
156
+ response = agent.get!(url)
157
+ if response.error?
158
+ @logger.error("Download failed", :error => response.status_line,
159
+ :url => url)
160
+ raise FPM::InvalidPackageConfiguration, "metacpan query failed"
161
+ end
162
+
163
+ File.open(build_path(tarball), "w") do |fd|
164
+ response.read_body { |c| fd.write(c) }
165
+ end
166
+ return build_path(tarball)
167
+ end # def download
168
+
169
+ def search(package, agent)
170
+ @logger.info("Asking metacpan about a module", :module => package)
171
+ metacpan_url = "http://api.metacpan.org/v0/module/" + package
172
+ response = agent.get!(metacpan_url)
173
+ if response.error?
174
+ @logger.error("metacpan query failed.", :error => response.status_line,
175
+ :module => package, :url => metacpan_url)
176
+ raise FPM::InvalidPackageConfiguration, "metacpan query failed"
177
+ end
178
+
179
+ data = ""
180
+ response.read_body { |c| data << c }
181
+ metadata = JSON.parse(data)
182
+ return metadata
183
+ end # def metadata
184
+
185
+ def fix_name(name)
186
+ return [attributes[:cpan_package_name_prefix], name].join("-").gsub("::", "-")
187
+ end # def fix_name
188
+
189
+ public(:input)
190
+ end # class FPM::Package::NPM
@@ -126,9 +126,14 @@ class FPM::Package::Deb < FPM::Package
126
126
  @architecture = %x{uname -m}.chomp
127
127
  end
128
128
  end
129
- if @architecture == "x86_64"
129
+
130
+ case @architecture
131
+ when "x86_64"
130
132
  # Debian calls x86_64 "amd64"
131
133
  @architecture = "amd64"
134
+ when "noarch"
135
+ # Debian calls noarch "all"
136
+ @architecture = "all"
132
137
  end
133
138
  return @architecture
134
139
  end # def architecture
@@ -432,7 +437,7 @@ class FPM::Package::Deb < FPM::Package
432
437
  # Make the control.tar.gz
433
438
  with(build_path("control.tar.gz")) do |controltar|
434
439
  @logger.info("Creating", :path => controltar, :from => control_path)
435
- safesystem(tar_cmd, "--numeric-owner", "--owner=0", "--group=0", "-zcf",
440
+ safesystem(tar_cmd, "--owner=root", "--group=root", "-zcf",
436
441
  controltar, "-C", control_path, ".")
437
442
  end
438
443
 
@@ -4,33 +4,95 @@ require "fpm/util"
4
4
  require "fileutils"
5
5
 
6
6
  class FPM::Package::NPM < FPM::Package
7
- def get_source(params)
8
- @npm = @paths.first
9
- end # def get_source
10
-
11
- def download(npm_name, version=nil)
12
- end # def download
13
-
14
- def get_metadata
15
- # set self[:...] values
16
- # :name
17
- # :maintainer
18
- # :url
19
- # :category
20
- # :dependencies
21
- end # def get_metadata
22
-
23
- def make_tarball!(tar_path, builddir)
24
- tmpdir = "#{tar_path}.dir"
25
- installdir = "#{tmpdir}/#{::Gem::dir}"
26
- ::FileUtils.mkdir_p(installdir)
27
- args = ["gem", "install", "--quiet", "--no-ri", "--no-rdoc",
28
- "--install-dir", installdir, "--ignore-dependencies", @paths.first]
29
- safesystem(*args)
30
- tar(tar_path, ".", tmpdir)
31
-
32
- # TODO(sissel): Make a helper method.
33
- safesystem(*["gzip", "-f", tar_path])
7
+ # Flags '--foo' will be accessable as attributes[:npm_foo]
8
+ option "--bin", "NPM_EXECUTABLE",
9
+ "The path to the npm executable you wish to run.", :default => "npm"
10
+
11
+ option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
12
+ "name with.", :default => "node"
13
+
14
+ private
15
+ def input(package)
16
+ # Notes:
17
+ # * npm respects PREFIX
18
+ settings = {
19
+ "cache" => build_path("npm_cache"),
20
+ "loglevel" => "warn",
21
+ "global" => "true"
22
+ }
23
+
24
+ if attributes.include?(:prefix) && !attributes[:prefix].nil?
25
+ settings["prefix"] = staging_path(attributes[:prefix])
26
+ else
27
+ @logger.info("Setting default npm install prefix",
28
+ :prefix => "/usr/local")
29
+ settings["prefix"] = staging_path("/usr/local")
30
+ end
31
+
32
+ FileUtils.mkdir_p(settings["prefix"])
33
+
34
+ npm_flags = []
35
+ settings.each do |key, value|
36
+ # npm lets you specify settings in a .npmrc but the same key=value settings
37
+ # are valid as '--key value' command arguments to npm. Woo!
38
+ @logger.debug("Configuring npm", key => value)
39
+ npm_flags += [ "--#{key}", value ]
40
+ end
41
+
42
+ install_args = [
43
+ attributes[:npm_bin],
44
+ "install",
45
+ # use 'package' or 'package@version'
46
+ (version ? "#{package}@#{version}" : package)
47
+ ]
48
+
49
+ # The only way to get npm to respect the 'prefix' setting appears to
50
+ # be to set the --global flag.
51
+ #install_args << "--global"
52
+ install_args += npm_flags
53
+
54
+ safesystem(*install_args)
55
+
56
+ # Query details about our now-installed package.
57
+ # We do this by using 'npm ls' with json + long enabled to query details
58
+ # about the installed package.
59
+ npm_ls_cmd = [attributes[:npm_bin], "ls", "--json", "--long", package] + npm_flags
60
+ npm_ls_fd = IO.popen(npm_ls_cmd)
61
+ npm_ls_out = npm_ls_fd.read()
62
+ pid, status = Process.waitpid2(npm_ls_fd.pid)
63
+ if !status.success?
64
+ raise FPM::Util::ProcessFailed.new("#{npm_ls_cmd.first} failed (exit " \
65
+ "code #{status.exitstatus}). " \
66
+ "Full command was: #{npm_ls_cmd.inspect}")
67
+ end
68
+ npm_ls = JSON.parse(npm_ls_out)
69
+ name, info = npm_ls["dependencies"].first
70
+
71
+ self.name = [attributes[:npm_package_name_prefix], name].join("-")
72
+ self.version = info["version"]
73
+
74
+ if info.include?("repository")
75
+ self.url = info["repository"]["url"]
76
+ else
77
+ self.url = "https://npmjs.org/package/#{self.name}"
78
+ end
79
+
80
+ self.description = info["description"]
81
+ self.vendor = sprintf("%s <%s>", info["author"]["name"],
82
+ info["author"]["email"])
83
+
84
+ # npm installs dependencies in the module itself, so if you do
85
+ # 'npm install express' it installs dependencies (like 'connect')
86
+ # to: node_modules/express/node_modules/connect/...
87
+ #
88
+ # To that end, I don't think we necessarily need to include
89
+ # any automatic dependency information since every 'npm install'
90
+ # is fully self-contained. That's why you don't see any bother, yet,
91
+ # to include the package's dependencies in here.
92
+ #
93
+ # It's possible someone will want to decouple that in the future,
94
+ # but I will wait for that feature request.
34
95
  end
35
96
 
97
+ public(:input)
36
98
  end # class FPM::Package::NPM