fpm-itchio 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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