fpm-itchio 1.4.0

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELIST +629 -0
  3. data/CONTRIBUTORS +26 -0
  4. data/LICENSE +21 -0
  5. data/bin/fpm +8 -0
  6. data/lib/fpm.rb +18 -0
  7. data/lib/fpm/command.rb +642 -0
  8. data/lib/fpm/errors.rb +4 -0
  9. data/lib/fpm/namespace.rb +4 -0
  10. data/lib/fpm/package.rb +524 -0
  11. data/lib/fpm/package/cpan.rb +378 -0
  12. data/lib/fpm/package/deb.rb +887 -0
  13. data/lib/fpm/package/dir.rb +207 -0
  14. data/lib/fpm/package/empty.rb +13 -0
  15. data/lib/fpm/package/gem.rb +224 -0
  16. data/lib/fpm/package/npm.rb +120 -0
  17. data/lib/fpm/package/osxpkg.rb +164 -0
  18. data/lib/fpm/package/p5p.rb +124 -0
  19. data/lib/fpm/package/pacman.rb +397 -0
  20. data/lib/fpm/package/pear.rb +117 -0
  21. data/lib/fpm/package/pkgin.rb +35 -0
  22. data/lib/fpm/package/puppet.rb +120 -0
  23. data/lib/fpm/package/pyfpm/__init__.py +1 -0
  24. data/lib/fpm/package/pyfpm/get_metadata.py +104 -0
  25. data/lib/fpm/package/python.rb +317 -0
  26. data/lib/fpm/package/rpm.rb +583 -0
  27. data/lib/fpm/package/sh.rb +69 -0
  28. data/lib/fpm/package/solaris.rb +95 -0
  29. data/lib/fpm/package/tar.rb +74 -0
  30. data/lib/fpm/package/virtualenv.rb +145 -0
  31. data/lib/fpm/package/zip.rb +63 -0
  32. data/lib/fpm/rake_task.rb +59 -0
  33. data/lib/fpm/util.rb +253 -0
  34. data/lib/fpm/version.rb +3 -0
  35. data/templates/deb.erb +52 -0
  36. data/templates/deb/changelog.erb +5 -0
  37. data/templates/deb/ldconfig.sh.erb +13 -0
  38. data/templates/deb/postinst_upgrade.sh.erb +62 -0
  39. data/templates/deb/postrm_upgrade.sh.erb +46 -0
  40. data/templates/deb/preinst_upgrade.sh.erb +41 -0
  41. data/templates/deb/prerm_upgrade.sh.erb +39 -0
  42. data/templates/osxpkg.erb +11 -0
  43. data/templates/p5p_metadata.erb +12 -0
  44. data/templates/pacman.erb +47 -0
  45. data/templates/pacman/INSTALL.erb +41 -0
  46. data/templates/puppet/package.pp.erb +34 -0
  47. data/templates/puppet/package/remove.pp.erb +13 -0
  48. data/templates/rpm.erb +261 -0
  49. data/templates/rpm/filesystem_list +14514 -0
  50. data/templates/sh.erb +367 -0
  51. data/templates/solaris.erb +15 -0
  52. metadata +265 -0
@@ -0,0 +1,207 @@
1
+ require "fpm/package"
2
+ require "fpm/util"
3
+ require "backports"
4
+ require "fileutils"
5
+ require "find"
6
+ require "socket"
7
+
8
+ # A directory package.
9
+ #
10
+ # This class supports both input and output. As a note, 'output' will
11
+ # only emit the files, not any metadata. This is an effective way
12
+ # to extract another package type.
13
+ class FPM::Package::Dir < FPM::Package
14
+ private
15
+
16
+ # Add a new path to this package.
17
+ #
18
+ # A special handling of the path occurs if it includes a '=' symbol.
19
+ # You can say "source=destination" and it will copy files from that source
20
+ # to the given destination in the package.
21
+ #
22
+ # This lets you take a local directory and map it to the desired location at
23
+ # packaging time. Such as: "./src/redis-server=/usr/local/bin" will make
24
+ # the local file ./src/redis-server appear as /usr/local/bin/redis-server in
25
+ # your package.
26
+ #
27
+ # If the path is a directory, it is copied recursively. The behavior
28
+ # of the copying is modified by the :chdir and :prefix attributes.
29
+ #
30
+ # If :prefix is set, the destination path is prefixed with that value.
31
+ # If :chdir is set, the current directory is changed to that value
32
+ # during the copy.
33
+ #
34
+ # Example: Copy /etc/X11 into this package as /opt/xorg/X11:
35
+ #
36
+ # package.attributes[:prefix] = "/opt/xorg"
37
+ # package.attributes[:chdir] = "/etc"
38
+ # package.input("X11")
39
+ def input(path)
40
+ chdir = attributes[:chdir] || "."
41
+
42
+ # Support mapping source=dest
43
+ # This mapping should work the same way 'rsync -a' does
44
+ # Meaning 'rsync -a source dest'
45
+ # and 'source=dest' in fpm work the same as the above rsync
46
+ if path =~ /.=./ && !File.exists?(chdir == '.' ? path : File.join(chdir, path))
47
+ origin, destination = path.split("=", 2)
48
+
49
+ if File.directory?(origin) && origin[-1,1] == "/"
50
+ chdir = chdir == '.' ? origin : File.join(chdir, origin)
51
+ source = "."
52
+ else
53
+ origin_dir = File.dirname(origin)
54
+ chdir = chdir == '.' ? origin_dir : File.join(chdir, origin_dir)
55
+ source = File.basename(origin)
56
+ end
57
+ else
58
+ source, destination = path, "/"
59
+ end
60
+
61
+ if attributes[:prefix]
62
+ destination = File.join(attributes[:prefix], destination)
63
+ end
64
+
65
+ destination = File.join(staging_path, destination)
66
+
67
+ logger["method"] = "input"
68
+ begin
69
+ ::Dir.chdir(chdir) do
70
+ begin
71
+ clone(source, destination)
72
+ rescue Errno::ENOENT => e
73
+ raise FPM::InvalidPackageConfiguration,
74
+ "Cannot package the path '#{File.join(chdir, source)}', does it exist?"
75
+ end
76
+ end
77
+ rescue Errno::ENOENT => e
78
+ raise FPM::InvalidPackageConfiguration,
79
+ "Cannot chdir to '#{chdir}'. Does it exist?"
80
+ end
81
+
82
+ # Set some defaults. This is useful because other package types
83
+ # can include license data from themselves (rpms, gems, etc),
84
+ # but to make sure a simple dir -> rpm works without having
85
+ # to specify a license.
86
+ self.license = "unknown"
87
+ self.vendor = [ENV["USER"], Socket.gethostname].join("@")
88
+ ensure
89
+ # Clean up any logger context we added.
90
+ logger.remove("method")
91
+ end # def input
92
+
93
+ # Output this package to the given directory.
94
+ def output(output_path)
95
+ output_check(output_path)
96
+
97
+ output_path = File.expand_path(output_path)
98
+ ::Dir.chdir(staging_path) do
99
+ logger["method"] = "output"
100
+ clone(".", output_path)
101
+ end
102
+ ensure
103
+ logger.remove("method")
104
+ end # def output
105
+
106
+ private
107
+ # Copy a file or directory to a destination
108
+ #
109
+ # This is special because it respects the full path of the source.
110
+ # Aditionally, hardlinks will be used instead of copies.
111
+ #
112
+ # Example:
113
+ #
114
+ # clone("/tmp/hello/world", "/tmp/example")
115
+ #
116
+ # The above will copy, recursively, /tmp/hello/world into
117
+ # /tmp/example/hello/world
118
+ def clone(source, destination)
119
+ logger.debug("Cloning path", :source => source, :destination => destination)
120
+ # Edge case check; abort if the temporary directory is the source.
121
+ # If the temporary dir is the same path as the source, it causes
122
+ # fpm to recursively (and forever) copy the staging directory by
123
+ # accident (#542).
124
+ if File.expand_path(source) == File.expand_path(::Dir.tmpdir)
125
+ raise FPM::InvalidPackageConfiguration,
126
+ "A source directory cannot be the root of your temporary " \
127
+ "directory (#{::Dir.tmpdir}). fpm uses the temporary directory " \
128
+ "to stage files during packaging, so this setting would have " \
129
+ "caused fpm to loop creating staging directories and copying " \
130
+ "them into your package! Oops! If you are confused, maybe you could " \
131
+ "check your TMPDIR, TMP, or TEMP environment variables?"
132
+ end
133
+
134
+ # For single file copies, permit file destinations
135
+ fileinfo = File.lstat(source)
136
+ if fileinfo.file? && !File.directory?(destination)
137
+ if destination[-1,1] == "/"
138
+ copy(source, File.join(destination, source))
139
+ else
140
+ copy(source, destination)
141
+ end
142
+ elsif fileinfo.symlink?
143
+ copy(source, File.join(destination, source))
144
+ else
145
+ # Copy all files from 'path' into staging_path
146
+ Find.find(source) do |path|
147
+ target = File.join(destination, path)
148
+ copy(path, target)
149
+ end
150
+ end
151
+ end # def clone
152
+
153
+ # Copy a path.
154
+ #
155
+ # Files will be hardlinked if possible, but copied otherwise.
156
+ # Symlinks should be copied as symlinks.
157
+ def copy(source, destination)
158
+ logger.debug("Copying path", :source => source, :destination => destination)
159
+ directory = File.dirname(destination)
160
+ # lstat to follow symlinks
161
+ dstat = File.stat(directory) rescue nil
162
+ if dstat.nil?
163
+ FileUtils.mkdir_p(directory)
164
+ elsif dstat.directory?
165
+ # do nothing, it's already a directory!
166
+ else
167
+ # It exists and is not a directory. This is probably a user error or a bug.
168
+ readable_path = directory.gsub(staging_path, "")
169
+ logger.error("You wanted to copy a file into a directory, but that's not a directory, it's a file!", :path => readable_path, :stat => dstat)
170
+ raise FPM::InvalidPackageConfiguration, "Tried to treat #{readable_path} like a directory, but it's a file!"
171
+ end
172
+
173
+ if File.directory?(source)
174
+ if !File.symlink?(source)
175
+ # Create a directory if this path is a directory
176
+ logger.debug("Creating", :directory => destination)
177
+ if !File.directory?(destination)
178
+ FileUtils.mkdir(destination)
179
+ end
180
+ else
181
+ # Linking symlinked directories causes a hardlink to be created, which
182
+ # results in the source directory being wiped out during cleanup,
183
+ # so copy the symlink.
184
+ logger.debug("Copying symlinked directory", :source => source,
185
+ :destination => destination)
186
+ FileUtils.copy_entry(source, destination)
187
+ end
188
+ else
189
+ # Otherwise try copying the file.
190
+ begin
191
+ logger.debug("Linking", :source => source, :destination => destination)
192
+ File.link(source, destination)
193
+ rescue Errno::ENOENT, Errno::EXDEV, Errno::EPERM
194
+ # Hardlink attempt failed, copy it instead
195
+ logger.debug("Copying", :source => source, :destination => destination)
196
+ copy_entry(source, destination)
197
+ rescue Errno::EEXIST
198
+ sane_path = destination.gsub(staging_path, "")
199
+ logger.error("Cannot copy file, the destination path is probably a directory and I attempted to write a file.", :path => sane_path, :staging => staging_path)
200
+ end
201
+ end
202
+
203
+ copy_metadata(source, destination)
204
+ end # def copy
205
+
206
+ public(:input, :output)
207
+ end # class FPM::Package::Dir
@@ -0,0 +1,13 @@
1
+ require "fpm/package"
2
+ require "backports"
3
+
4
+ # Empty Package type. For strict/meta/virtual package creation
5
+
6
+ class FPM::Package::Empty < FPM::Package
7
+ def output(output_path)
8
+ logger.warn("Your package has gone into the void.")
9
+ end
10
+ def to_s(fmt)
11
+ return ""
12
+ end
13
+ end
@@ -0,0 +1,224 @@
1
+ require "fpm/namespace"
2
+ require "fpm/package"
3
+ require "rubygems"
4
+ require "fileutils"
5
+ require "fpm/util"
6
+ require "yaml"
7
+
8
+ # A rubygems package.
9
+ #
10
+ # This does not currently support 'output'
11
+ #
12
+ # The following attributes are supported:
13
+ #
14
+ # * :gem_bin_path
15
+ # * :gem_package_name_prefix
16
+ # * :gem_gem
17
+ class FPM::Package::Gem < FPM::Package
18
+ # Flags '--foo' will be accessable as attributes[:gem_foo]
19
+ option "--bin-path", "DIRECTORY", "The directory to install gem executables"
20
+ option "--package-prefix", "NAMEPREFIX",
21
+ "(DEPRECATED, use --package-name-prefix) Name to prefix the package " \
22
+ "name with." do |value|
23
+ logger = Cabin::Channel.get
24
+ logger.warn("Using deprecated flag: --package-prefix. Please use " \
25
+ "--package-name-prefix")
26
+ value
27
+ end
28
+ option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
29
+ "name with.", :default => "rubygem"
30
+ option "--gem", "PATH_TO_GEM",
31
+ "The path to the 'gem' tool (defaults to 'gem' and searches " \
32
+ "your $PATH)", :default => "gem"
33
+ option "--fix-name", :flag, "Should the target package name be prefixed?",
34
+ :default => true
35
+ option "--fix-dependencies", :flag, "Should the package dependencies be " \
36
+ "prefixed?", :default => true
37
+ option "--env-shebang", :flag, "Should the target package have the " \
38
+ "shebang rewritten to use env?", :default => true
39
+
40
+ option "--prerelease", :flag, "Allow prerelease versions of a gem", :default => false
41
+ option "--disable-dependency", "gem_name",
42
+ "The gem name to remove from dependency list",
43
+ :multivalued => true, :attribute_name => :gem_disable_dependencies
44
+
45
+ option "--version-bins", :flag, "Append the version to the bins", :default => false
46
+
47
+ def input(gem)
48
+ # 'arg' is the name of the rubygem we should unpack.
49
+ path_to_gem = download_if_necessary(gem, version)
50
+
51
+ # Got a good gem now (downloaded or otherwise)
52
+ #
53
+ # 1. unpack it into staging_path
54
+ # 2. take the metadata from it and update our wonderful package with it.
55
+ load_package_info(path_to_gem)
56
+ install_to_staging(path_to_gem)
57
+ end # def input
58
+
59
+ def download_if_necessary(gem, gem_version)
60
+ path = gem
61
+ if !File.exist?(path)
62
+ path = download(gem, gem_version)
63
+ end
64
+
65
+ logger.info("Using gem file", :path => path)
66
+ return path
67
+ end # def download_if_necessary
68
+
69
+ def download(gem_name, gem_version=nil)
70
+
71
+ logger.info("Trying to download", :gem => gem_name, :version => gem_version)
72
+
73
+ gem_fetch = [ "#{attributes[:gem_gem]}", "fetch", gem_name]
74
+
75
+ gem_fetch += ["--prerelease"] if attributes[:gem_prerelease?]
76
+ gem_fetch += ["--version", gem_version] if gem_version
77
+
78
+ download_dir = build_path(gem_name)
79
+ FileUtils.mkdir(download_dir) unless File.directory?(download_dir)
80
+
81
+ ::Dir.chdir(download_dir) do |dir|
82
+ logger.debug("Downloading in directory #{dir}")
83
+ safesystem(*gem_fetch)
84
+ end
85
+
86
+ gem_files = ::Dir.glob(File.join(download_dir, "*.gem"))
87
+
88
+ if gem_files.length != 1
89
+ raise "Unexpected number of gem files in #{download_dir}, #{gem_files.length} should be 1"
90
+ end
91
+
92
+ return gem_files.first
93
+ end # def download
94
+
95
+ def load_package_info(gem_path)
96
+
97
+ spec = YAML.load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml})
98
+
99
+ if !attributes[:gem_package_prefix].nil?
100
+ attributes[:gem_package_name_prefix] = attributes[:gem_package_prefix]
101
+ end
102
+
103
+ # name prefixing is optional, if enabled, a name 'foo' will become
104
+ # 'rubygem-foo' (depending on what the gem_package_name_prefix is)
105
+ self.name = spec.name
106
+ if attributes[:gem_fix_name?]
107
+ self.name = fix_name(spec.name)
108
+ end
109
+
110
+ #self.name = [attributes[:gem_package_name_prefix], spec.name].join("-")
111
+ self.license = (spec.license or "no license listed in #{File.basename(gem_path)}")
112
+
113
+ # expand spec's version to match RationalVersioningPolicy to prevent cases
114
+ # where missing 'build' number prevents correct dependency resolution by target
115
+ # package manager. Ie. for dpkg 1.1 != 1.1.0
116
+ m = spec.version.to_s.scan(/(\d+)\.?/)
117
+ self.version = m.flatten.fill('0', m.length..2).join('.')
118
+
119
+ self.vendor = spec.author
120
+ self.url = spec.homepage
121
+ self.category = "Languages/Development/Ruby"
122
+
123
+ # if the gemspec has C extensions defined, then this should be a 'native' arch.
124
+ if !spec.extensions.empty?
125
+ self.architecture = "native"
126
+ else
127
+ self.architecture = "all"
128
+ end
129
+
130
+ # make sure we have a description
131
+ description_options = [ spec.description, spec.summary, "#{spec.name} - no description given" ]
132
+ self.description = description_options.find { |d| !(d.nil? or d.strip.empty?) }
133
+
134
+ # Upstream rpms seem to do this, might as well share.
135
+ # TODO(sissel): Figure out how to hint this only to rpm?
136
+ # maybe something like attributes[:rpm_provides] for rpm specific stuff?
137
+ # Or just ignore it all together.
138
+ #self.provides << "rubygem(#{self.name})"
139
+
140
+ # By default, we'll usually automatically provide this, but in the case that we are
141
+ # composing multiple packages, it's best to explicitly include it in the provides list.
142
+ self.provides << "#{self.name} = #{self.version}"
143
+
144
+ if !attributes[:no_auto_depends?]
145
+ spec.runtime_dependencies.map do |dep|
146
+ # rubygems 1.3.5 doesn't have 'Gem::Dependency#requirement'
147
+ if dep.respond_to?(:requirement)
148
+ reqs = dep.requirement.to_s
149
+ else
150
+ reqs = dep.version_requirements
151
+ end
152
+
153
+ # Some reqs can be ">= a, < b" versions, let's handle that.
154
+ reqs.to_s.split(/, */).each do |req|
155
+ if attributes[:gem_disable_dependencies]
156
+ next if attributes[:gem_disable_dependencies].include?(dep.name)
157
+ end
158
+
159
+ if attributes[:gem_fix_dependencies?]
160
+ name = fix_name(dep.name)
161
+ else
162
+ name = dep.name
163
+ end
164
+ self.dependencies << "#{name} #{req}"
165
+ end
166
+ end # runtime_dependencies
167
+ end #no_auto_depends
168
+ end # def load_package_info
169
+
170
+ def install_to_staging(gem_path)
171
+ if attributes.include?(:prefix) && ! attributes[:prefix].nil?
172
+ installdir = "#{staging_path}/#{attributes[:prefix]}"
173
+ else
174
+ gemdir = safesystemout(*[attributes[:gem_gem], 'env', 'gemdir']).chomp
175
+ installdir = File.join(staging_path, gemdir)
176
+ end
177
+
178
+ ::FileUtils.mkdir_p(installdir)
179
+ # TODO(sissel): Allow setting gem tool path
180
+ args = [attributes[:gem_gem], "install", "--quiet", "--no-ri", "--no-rdoc",
181
+ "--install-dir", installdir, "--ignore-dependencies"]
182
+ if attributes[:gem_env_shebang?]
183
+ args += ["-E"]
184
+ end
185
+
186
+ if attributes.include?(:gem_bin_path) && ! attributes[:gem_bin_path].nil?
187
+ bin_path = File.join(staging_path, attributes[:gem_bin_path])
188
+ else
189
+ gem_env = safesystemout(*[attributes[:gem_gem], 'env']).split("\n")
190
+ gem_bin = gem_env.select{ |line| line =~ /EXECUTABLE DIRECTORY/ }.first.split(': ').last
191
+ bin_path = File.join(staging_path, gem_bin)
192
+ end
193
+
194
+ args += ["--bindir", bin_path]
195
+ ::FileUtils.mkdir_p(bin_path)
196
+ args << gem_path
197
+ safesystem(*args)
198
+
199
+ # Delete bin_path if it's empty, and any empty parents (#612)
200
+ # Above, we mkdir_p bin_path because rubygems aborts if the parent
201
+ # directory doesn't exist, for example:
202
+ # ERROR: While executing gem ... (Errno::ENOENT)
203
+ # No such file or directory - /tmp/something/weird/bin
204
+ tmp = bin_path
205
+ while ::Dir.entries(tmp).size == 2 || tmp == "/" # just [ "..", "." ] is an empty directory
206
+ logger.info("Deleting empty bin_path", :path => tmp)
207
+ ::Dir.rmdir(tmp)
208
+ tmp = File.dirname(tmp)
209
+ end
210
+ if attributes[:gem_version_bins?] and File.directory?(bin_path)
211
+ (::Dir.entries(bin_path) - ['.','..']).each do |bin|
212
+ FileUtils.mv("#{bin_path}/#{bin}", "#{bin_path}/#{bin}-#{self.version}")
213
+ end
214
+ end
215
+ end # def install_to_staging
216
+
217
+ # Sanitize package name.
218
+ # This prefixes the package name with 'rubygem' (but depends on the attribute
219
+ # :gem_package_name_prefix
220
+ def fix_name(name)
221
+ return [attributes[:gem_package_name_prefix], name].join("-")
222
+ end # def fix_name
223
+ public(:input, :output)
224
+ end # class FPM::Package::Gem
@@ -0,0 +1,120 @@
1
+ require "fpm/namespace"
2
+ require "fpm/package"
3
+ require "fpm/util"
4
+ require "fileutils"
5
+
6
+ class FPM::Package::NPM < FPM::Package
7
+ class << self
8
+ include FPM::Util
9
+ end
10
+ # Flags '--foo' will be accessable as attributes[:npm_foo]
11
+ option "--bin", "NPM_EXECUTABLE",
12
+ "The path to the npm executable you wish to run.", :default => "npm"
13
+
14
+ option "--package-name-prefix", "PREFIX", "Name to prefix the package " \
15
+ "name with.", :default => "node"
16
+
17
+ option "--registry", "NPM_REGISTRY",
18
+ "The npm registry to use instead of the default."
19
+
20
+ private
21
+ def input(package)
22
+ # Notes:
23
+ # * npm respects PREFIX
24
+ settings = {
25
+ "cache" => build_path("npm_cache"),
26
+ "loglevel" => "warn",
27
+ "global" => "true"
28
+ }
29
+
30
+ settings["registry"] = attributes[:npm_registry] if attributes[:npm_registry_given?]
31
+ set_default_prefix unless attributes[:prefix_given?]
32
+
33
+ settings["prefix"] = staging_path(attributes[:prefix])
34
+ FileUtils.mkdir_p(settings["prefix"])
35
+
36
+ npm_flags = []
37
+ settings.each do |key, value|
38
+ # npm lets you specify settings in a .npmrc but the same key=value settings
39
+ # are valid as '--key value' command arguments to npm. Woo!
40
+ logger.debug("Configuring npm", key => value)
41
+ npm_flags += [ "--#{key}", value ]
42
+ end
43
+
44
+ install_args = [
45
+ attributes[:npm_bin],
46
+ "install",
47
+ # use 'package' or 'package@version'
48
+ (version ? "#{package}@#{version}" : package)
49
+ ]
50
+
51
+ # The only way to get npm to respect the 'prefix' setting appears to
52
+ # be to set the --global flag.
53
+ #install_args << "--global"
54
+ install_args += npm_flags
55
+
56
+ safesystem(*install_args)
57
+
58
+ # Query details about our now-installed package.
59
+ # We do this by using 'npm ls' with json + long enabled to query details
60
+ # about the installed package.
61
+ npm_ls_out = safesystemout(attributes[:npm_bin], "ls", "--json", "--long", *npm_flags)
62
+ npm_ls = JSON.parse(npm_ls_out)
63
+ name, info = npm_ls["dependencies"].first
64
+
65
+ self.name = [attributes[:npm_package_name_prefix], name].join("-")
66
+ self.version = info.fetch("version", "0.0.0")
67
+
68
+ if info.include?("repository")
69
+ self.url = info["repository"]["url"]
70
+ else
71
+ self.url = "https://npmjs.org/package/#{self.name}"
72
+ end
73
+
74
+ self.description = info["description"]
75
+ # Supposedly you can upload a package for npm with no author/author email
76
+ # so I'm being safer with this. Author can also be a hash or a string
77
+ self.vendor = "Unknown <unknown@unknown.unknown>"
78
+ if info.include?("author")
79
+ author_info = info["author"]
80
+ # If a hash, assemble into a string
81
+ if author_info.respond_to? :fetch
82
+ self.vendor = sprintf("%s <%s>", author_info.fetch("name", "unknown"),
83
+ author_info.fetch("email", "unknown@unknown.unknown"))
84
+ else
85
+ # Probably will need a better check for validity here
86
+ self.vendor = author_info unless author_info == ""
87
+ end
88
+ end
89
+
90
+ # npm installs dependencies in the module itself, so if you do
91
+ # 'npm install express' it installs dependencies (like 'connect')
92
+ # to: node_modules/express/node_modules/connect/...
93
+ #
94
+ # To that end, I don't think we necessarily need to include
95
+ # any automatic dependency information since every 'npm install'
96
+ # is fully self-contained. That's why you don't see any bother, yet,
97
+ # to include the package's dependencies in here.
98
+ #
99
+ # It's possible someone will want to decouple that in the future,
100
+ # but I will wait for that feature request.
101
+ end
102
+
103
+ def set_default_prefix
104
+ attributes[:prefix] = self.class.default_prefix
105
+ attributes[:prefix_given?] = true
106
+ end
107
+
108
+ def self.default_prefix
109
+ npm_prefix = safesystemout("npm", "prefix", "-g").chomp
110
+ if npm_prefix.count("\n") > 0
111
+ raise FPM::InvalidPackageConfiguration, "`npm prefix -g` returned unexpected output."
112
+ elsif !File.directory?(npm_prefix)
113
+ raise FPM::InvalidPackageConfiguration, "`npm prefix -g` returned a non-existent directory"
114
+ end
115
+ logger.info("Setting default npm install prefix", :prefix => npm_prefix)
116
+ npm_prefix
117
+ end
118
+
119
+ public(:input)
120
+ end # class FPM::Package::NPM