Class: FPM::Package::Gem
- Inherits:
-
FPM::Package
- Object
- FPM::Package
- FPM::Package::Gem
- Defined in:
- lib/fpm/package/gem.rb
Overview
A rubygems package.
This does not currently support ‘output’
The following attributes are supported:
-
:gem_bin_path
-
:gem_package_name_prefix
-
:gem_gem
Constant Summary collapse
- GEMSPEC_YAML_CLASSES =
def download
[ ::Gem::Specification, ::Gem::Version, Time, ::Gem::Dependency, ::Gem::Requirement, Symbol ]
- P_RE_LEADIN =
Regular expression to accept a gem changelog line, and store date & version, if any, in named capture groups. Supports formats suggested by keepachangelog.com and github.com/tech-angels/vandamme as well as other similar formats that actually occur in the wild. Build it in pieces for readability, and allow version and date in either order. Whenever you change this, add a row to the test case in spec/fpm/package/gem_spec.rb. Don’t even try to handle dates that lack four-digit years. Building blocks:
'^[#=]{0,3}\s?'
- P_RE_VERSION_ =
'[\w\.-]+\.[\w\.-]+[a-zA-Z0-9]'
- P_RE_SEPARATOR =
'\s[-=/(]?\s?'
- P_RE_DATE1 =
'\d{4}-\d{2}-\d{2}'
- P_RE_DATE2 =
'\w+ \d{1,2}(?:st|nd|rd|th)?,\s\d{4}'
- P_RE_DATE3 =
'\w+\s+\w+\s+\d{1,2},\s\d{4}'
- P_RE_DATE =
"(?<date>#{P_RE_DATE1}|#{P_RE_DATE2}|#{P_RE_DATE3})"
- P_RE_URL =
In parens, per markdown
'\(https?:[-\w/.%]*\)'
- P_RE_GTMAGIC =
github magic version diff, per chandler
'\[\]'
- P_RE_VERSION =
"\\[?(?:Version |v)?(?<version>#{P_RE_VERSION_})\\]?(?:#{P_RE_URL}|#{P_RE_GTMAGIC})?"
- P_RE_VERSION_DATE =
The final RE’s:
"#{P_RE_LEADIN}#{P_RE_VERSION}#{P_RE_SEPARATOR}#{P_RE_DATE}"
- P_RE_DATE_VERSION =
"#{P_RE_LEADIN}#{P_RE_DATE}#{P_RE_SEPARATOR}#{P_RE_VERSION}"
Instance Attribute Summary
Attributes inherited from FPM::Package
#architecture, #attributes, #attrs, #category, #config_files, #conflicts, #dependencies, #description, #directories, #epoch, #iteration, #license, #maintainer, #name, #provides, #replaces, #scripts, #url, #vendor, #version
Instance Method Summary collapse
-
#detect_source_date_from_changelog(installdir) ⇒ Object
Detect release date, if found, store in attributes.
-
#download(gem_name, gem_version = nil) ⇒ Object
def download_if_necessary.
-
#download_if_necessary(gem, gem_version) ⇒ Object
def input.
-
#fix_name(name) ⇒ Object
Sanitize package name.
-
#input(gem) ⇒ Object
def staging_path.
-
#install_to_staging(gem_path) ⇒ Object
def load_package_info.
- #load_package_info(gem_path) ⇒ Object
-
#staging_path(path = nil) ⇒ Object
Override parent method.
Methods inherited from FPM::Package
apply_options, #build_path, #cleanup, #cleanup_build, #cleanup_staging, #convert, #converted_from, default_attributes, #edit_file, #files, inherited, #initialize, option, #output, #script, #to_s, #type, type, types
Methods included from Util
#ar_cmd, #ar_cmd_deterministic?, #copied_entries, #copy_entry, #copy_metadata, #default_shell, #erbnew, #execmd, #expand_pessimistic_constraints, #logger, #program_exists?, #program_in_path?, #safesystem, #safesystemout, #tar_cmd, #tar_cmd_supports_sort_names_and_set_mtime?
Constructor Details
This class inherits a constructor from FPM::Package
Instance Method Details
#detect_source_date_from_changelog(installdir) ⇒ Object
Detect release date, if found, store in attributes
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
# File 'lib/fpm/package/gem.rb', line 339 def detect_source_date_from_changelog(installdir) name = self.name.sub("rubygem-", "") + "-" + self.version changelog = nil datestr = nil r1 = Regexp.new(P_RE_VERSION_DATE) r2 = Regexp.new(P_RE_DATE_VERSION) # Changelog doesn't have a standard name, so check all common variations # Sort this list using LANG=C, i.e. caps first [ "CHANGELIST", "CHANGELOG", "CHANGELOG.asciidoc", "CHANGELOG.md", "CHANGELOG.rdoc", "CHANGELOG.rst", "CHANGELOG.txt", "CHANGES", "CHANGES.md", "CHANGES.txt", "ChangeLog", "ChangeLog.md", "ChangeLog.txt", "Changelog", "Changelog.md", "Changelog.txt", "changelog", "changelog.md", "changelog.txt", ].each do |changelogname| path = File.join(installdir, "gems", name, changelogname) if File.exist?(path) changelog = path File.open path do |file| file.each_line do |line| if line =~ /#{self.version}/ [r1, r2].each do |r| if r.match(line) datestr = $~[:date] break end end end end end end end if datestr date = Date.parse(datestr) sec = date.strftime("%s") attributes[:source_date_epoch] = sec logger.debug("Gem %s has changelog date %s, setting source_date_epoch to %s" % [name, datestr, sec]) elsif changelog logger.debug("Gem %s changelog %s did not have recognizable date for release %s" % [name, changelog, self.version]) else logger.debug("Gem %s did not have changelog with recognized name" % [name]) # FIXME: check rubygems.org? end end |
#download(gem_name, gem_version = nil) ⇒ Object
def download_if_necessary
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 |
# File 'lib/fpm/package/gem.rb', line 99 def download(gem_name, gem_version=nil) logger.info("Trying to download", :gem => gem_name, :version => gem_version) download_dir = build_path(gem_name) FileUtils.mkdir(download_dir) unless File.directory?(download_dir) if attributes[:gem_git_repo] logger.debug("Git cloning in directory #{download_dir}") safesystem("git", "-C", download_dir, "clone", attributes[:gem_git_repo], ".") if attributes[:gem_git_branch] safesystem("git", "-C", download_dir, "checkout", attributes[:gem_git_branch]) end gem_build = [ "#{attributes[:gem_gem]}", "build", "#{download_dir}/#{gem_name}.gemspec"] ::Dir.chdir(download_dir) do |dir| logger.debug("Building in directory #{dir}") safesystem(*gem_build) end gem_files = ::Dir.glob(File.join(download_dir, "*.gem")) else gem_fetch = [ "#{attributes[:gem_gem]}", "fetch", gem_name] gem_fetch += ["--prerelease"] if attributes[:gem_prerelease?] gem_fetch += ["--version", gem_version] if gem_version ::Dir.chdir(download_dir) do |dir| logger.debug("Downloading in directory #{dir}") safesystem(*gem_fetch) end gem_files = ::Dir.glob(File.join(download_dir, "*.gem")) end if gem_files.length != 1 raise "Unexpected number of gem files in #{download_dir}, #{gem_files.length} should be 1" end return gem_files.first end |
#download_if_necessary(gem, gem_version) ⇒ Object
def input
89 90 91 92 93 94 95 96 97 |
# File 'lib/fpm/package/gem.rb', line 89 def download_if_necessary(gem, gem_version) path = gem if !File.exist?(path) path = download(gem, gem_version) end logger.info("Using gem file", :path => path) return path end |
#fix_name(name) ⇒ Object
Sanitize package name. This prefixes the package name with ‘rubygem’ (but depends on the attribute :gem_package_name_prefix
313 314 315 |
# File 'lib/fpm/package/gem.rb', line 313 def fix_name(name) return [attributes[:gem_package_name_prefix], name].join("-") end |
#input(gem) ⇒ Object
def staging_path
77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/fpm/package/gem.rb', line 77 def input(gem) # 'arg' is the name of the rubygem we should unpack. path_to_gem = download_if_necessary(gem, version) # Got a good gem now (downloaded or otherwise) # # 1. unpack it into staging_path # 2. take the metadata from it and update our wonderful package with it. load_package_info(path_to_gem) install_to_staging(path_to_gem) end |
#install_to_staging(gem_path) ⇒ Object
def load_package_info
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/fpm/package/gem.rb', line 222 def install_to_staging(gem_path) if attributes.include?(:prefix) && ! attributes[:prefix].nil? installdir = "#{staging_path}/#{attributes[:prefix]}" else gemdir = safesystemout(*[attributes[:gem_gem], 'env', 'gemdir']).chomp installdir = File.join(staging_path, gemdir) end ::FileUtils.mkdir_p(installdir) # TODO(sissel): Allow setting gem tool path args = [attributes[:gem_gem], "install", "--quiet", "--no-user-install", "--install-dir", installdir] if ::Gem::VERSION =~ /^[012]\./ args += [ "--no-ri", "--no-rdoc" ] else # Rubygems 3.0.0 changed --no-ri to --no-document args += [ "--no-document" ] end if !attributes[:gem_embed_dependencies?] args += ["--ignore-dependencies"] end if attributes[:gem_env_shebang?] args += ["-E"] end if attributes.include?(:gem_bin_path) && ! attributes[:gem_bin_path].nil? bin_path = File.join(staging_path, attributes[:gem_bin_path]) else gem_env = safesystemout(*[attributes[:gem_gem], 'env']).split("\n") gem_bin = gem_env.select{ |line| line =~ /EXECUTABLE DIRECTORY/ }.first.split(': ').last bin_path = File.join(staging_path, gem_bin) end args += ["--bindir", bin_path] ::FileUtils.mkdir_p(bin_path) args << gem_path safesystem(*args) # Replace the shebangs in the executables if attributes[:gem_shebang] ::Dir.entries(bin_path).each do |file_name| # exclude . and .. next if ['.', '..'].include?(file_name) # exclude everything which is not a file file_path = File.join(bin_path, file_name) next unless File.ftype(file_path) == 'file' # replace shebang in files if there is one file = File.read(file_path) if file.gsub!(/\A#!.*$/, "#!#{attributes[:gem_shebang]}") File.open(file_path, 'w'){|f| f << file} end end end # Delete bin_path if it's empty, and any empty parents (#612) # Above, we mkdir_p bin_path because rubygems aborts if the parent # directory doesn't exist, for example: # ERROR: While executing gem ... (Errno::ENOENT) # No such file or directory - /tmp/something/weird/bin tmp = bin_path while ::Dir.entries(tmp).size == 2 || tmp == "/" # just [ "..", "." ] is an empty directory logger.info("Deleting empty bin_path", :path => tmp) ::Dir.rmdir(tmp) tmp = File.dirname(tmp) end if attributes[:gem_version_bins?] and File.directory?(bin_path) (::Dir.entries(bin_path) - ['.','..']).each do |bin| FileUtils.mv("#{bin_path}/#{bin}", "#{bin_path}/#{bin}-#{self.version}") end end if attributes[:source_date_epoch_from_changelog?] detect_source_date_from_changelog(installdir) end # Remove generated Makefile and gem_make.out files, if any; they # are not needed, and may contain generated paths that cause # different output on successive runs. Find.find(installdir) do |path| if path =~ /.*(gem_make.out|Makefile|mkmf.log)$/ logger.info("Removing no longer needed file %s to reduce nondeterminism" % path) File.unlink(path) end end end |
#load_package_info(gem_path) ⇒ Object
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/fpm/package/gem.rb', line 138 def load_package_info(gem_path) # TODO(sissel): Maybe we should check if `safe_load` method exists instead of this version check? if ::Gem::Version.new(RUBY_VERSION) >= ::Gem::Version.new("3.1.0") # Ruby 3.1.0 switched to a Psych/YAML version that defaults to "safe" loading # and unfortunately `gem specification --yaml` emits YAML that requires # class loaders to process correctly spec = YAML.load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml}, :permitted_classes => GEMSPEC_YAML_CLASSES) else # Older versions of ruby call this method YAML.safe_load spec = YAML.safe_load(%x{#{attributes[:gem_gem]} specification #{gem_path} --yaml}, GEMSPEC_YAML_CLASSES) end if !attributes[:gem_package_prefix].nil? attributes[:gem_package_name_prefix] = attributes[:gem_package_prefix] end # name prefixing is optional, if enabled, a name 'foo' will become # 'rubygem-foo' (depending on what the gem_package_name_prefix is) self.name = spec.name if attributes[:gem_fix_name?] self.name = fix_name(spec.name) end #self.name = [attributes[:gem_package_name_prefix], spec.name].join("-") self.license = (spec.license or "no license listed in #{File.basename(gem_path)}") # expand spec's version to match RationalVersioningPolicy to prevent cases # where missing 'build' number prevents correct dependency resolution by target # package manager. Ie. for dpkg 1.1 != 1.1.0 m = spec.version.to_s.scan(/(\d+)\.?/) self.version = m.flatten.fill('0', m.length..2).join('.') self.vendor = spec. self.url = spec.homepage self.category = "Languages/Development/Ruby" # if the gemspec has C extensions defined, then this should be a 'native' arch. if !spec.extensions.empty? self.architecture = "native" else self.architecture = "all" end # make sure we have a description = [ spec.description, spec.summary, "#{spec.name} - no description given" ] self.description = .find { |d| !(d.nil? or d.strip.empty?) } # Upstream rpms seem to do this, might as well share. # TODO(sissel): Figure out how to hint this only to rpm? # maybe something like attributes[:rpm_provides] for rpm specific stuff? # Or just ignore it all together. #self.provides << "rubygem(#{self.name})" # By default, we'll usually automatically provide this, but in the case that we are # composing multiple packages, it's best to explicitly include it in the provides list. self.provides << "#{self.name} = #{self.version}" if !attributes[:no_auto_depends?] && !attributes[:gem_embed_dependencies?] spec.runtime_dependencies.map do |dep| # rubygems 1.3.5 doesn't have 'Gem::Dependency#requirement' if dep.respond_to?(:requirement) reqs = dep.requirement.to_s else reqs = dep.version_requirements end # Some reqs can be ">= a, < b" versions, let's handle that. reqs.to_s.split(/, */).each do |req| if attributes[:gem_disable_dependencies] next if attributes[:gem_disable_dependencies].include?(dep.name) end if attributes[:gem_fix_dependencies?] name = fix_name(dep.name) else name = dep.name end self.dependencies << "#{name} #{req}" end end # runtime_dependencies end #no_auto_depends end |
#staging_path(path = nil) ⇒ Object
Override parent method
66 67 68 69 70 71 72 73 74 75 |
# File 'lib/fpm/package/gem.rb', line 66 def staging_path(path=nil) @gem_staging_path ||= attributes[:gem_stagingdir] || Stud::Temporary.directory("package-#{type}-staging") @staging_path = @gem_staging_path if path.nil? return @staging_path else return File.join(@staging_path, path) end end |