Class: Simp::RPM
- Inherits:
-
Object
- Object
- Simp::RPM
- Defined in:
- lib/simp/rpm.rb
Overview
Set the environment variable ‘SIMP_RPM_dist` to ensure all packages use a particular dist.
An Simp::RPM instance represents RPM metadata extracted from an RPM or an RPM spec file.
Simp::RPM also contains class methods that are useful for processing RPMs in the SIMP build process.
Instance Attribute Summary collapse
-
#lua_debug ⇒ Object
readonly
Returns the value of attribute lua_debug.
-
#packages ⇒ Object
readonly
Returns the value of attribute packages.
-
#verbose ⇒ Object
readonly
Returns the value of attribute verbose.
Class Method Summary collapse
-
.copy_wo_vcs(start_dir, src, dest, dereference = true) ⇒ Object
Copies specific content from one directory to another.
- .create_rpm_build_metadata(project_dir, srpms = nil, rpms = nil) ⇒ Object
-
.execute(cmd) ⇒ Object
Executes a command and returns a hash with the exit status, stdout output and stderr output.
-
.get_info(rpm_source) ⇒ Object
Parses information, such as the version, from the given specfile or RPM into a hash.
- .indent(message, indent_length) ⇒ Object
- .rpm_cmd ⇒ Object
- .sh(args) ⇒ Object
- .system_dist ⇒ Object
-
.version ⇒ Object
Returns the version of RPM installed on the system.
Instance Method Summary collapse
- #arch(package = @packages.first) ⇒ Object
- #basename(package = @packages.first) ⇒ Object
-
#dist(package = @packages.first) ⇒ Object
‘dist` of the OS itself.
- #full_version(package = @packages.first) ⇒ Object
- #has_dist_tag?(package = @packages.first) ⇒ Boolean
-
#initialize(rpm_source) ⇒ RPM
constructor
Constructs a new Simp::RPM object.
- #name(package = @packages.first) ⇒ Object
-
#newer?(other_rpm) ⇒ Boolean
Returns whether or not the current RPM package is newer than the passed RPM.
-
#package_newer?(package, other_rpm) ⇒ Boolean
Returns whether or not the current RPM sub-package is newer than the passed RPM.
- #release(package = @packages.first) ⇒ Object
- #rpm_name(package = @packages.first) ⇒ Object
- #signature(package = @packages.first) ⇒ Object
- #system_dist ⇒ Object
-
#update_rpmmacros ⇒ Object
Work around the silliness with ‘centos’ being tacked onto things via the ‘dist’ flag.
- #valid_package?(package) ⇒ Boolean
- #version(package = @packages.first) ⇒ Object
Constructor Details
#initialize(rpm_source) ⇒ RPM
Constructs a new Simp::RPM object. Requires the path to the spec file, or RPM, from which information will be gathered.
When the information is from a spec file, multiple packages may exist.
The following information will be retrieved per package:
- basename
-
The name of the package (as it would be queried in yum)
- version
-
The version of the package
- release
-
The release version of the package
- full_version
-
The full version of the package: [version]-
- name
-
The full name of the package: [basename]-
- arch
-
The machine architecture of the package
- signature
-
The signature key of the package, if it exists. Will not
apply when +rpm_source+ is an RPM spec file.
- rpm_name
-
The full name of the rpm
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 |
# File 'lib/simp/rpm.rb', line 46 def initialize(rpm_source) @verbose = ENV.fetch('SIMP_RPM_verbose','no') =='yes' update_rpmmacros # Simp::RPM.get_info returns a Hash or an Array of Hashes. # Steps below prevent single Hash from implicitly being converted # to Array using Hash.to_a. info_array = [] info_array << Simp::RPM.get_info(rpm_source) info_array.flatten! @info = {} info_array.each do |package_info| @info[package_info[:basename]] = package_info end @packages = @info.keys if @verbose require 'pp' puts "== Simp::RPM @packages" puts @packages.pretty_inspect end end |
Instance Attribute Details
#lua_debug ⇒ Object (readonly)
Returns the value of attribute lua_debug.
17 18 19 |
# File 'lib/simp/rpm.rb', line 17 def lua_debug @lua_debug end |
#packages ⇒ Object (readonly)
Returns the value of attribute packages.
17 18 19 |
# File 'lib/simp/rpm.rb', line 17 def packages @packages end |
#verbose ⇒ Object (readonly)
Returns the value of attribute verbose.
17 18 19 |
# File 'lib/simp/rpm.rb', line 17 def verbose @verbose end |
Class Method Details
.copy_wo_vcs(start_dir, src, dest, dereference = true) ⇒ Object
Copies specific content from one directory to another.
- start_dir
-
the root directory where the original files are located within
- src
-
a pattern given to find(1) to match against the desired files to copy
- dest
-
the destination directory to receive the copies
275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/simp/rpm.rb', line 275 def self.copy_wo_vcs(start_dir, src, dest, dereference=true) if dereference.nil? || dereference dereference = "--dereference" else dereference = "" end Dir.chdir(start_dir) do sh %{find #{src} \\( -path "*/.svn" -a -type d -o -path "*/.git*" \\) -prune -o -print | cpio -u --warning none --quiet --make-directories #{dereference} -p "#{dest}" 2>&1 > /dev/null} end end |
.create_rpm_build_metadata(project_dir, srpms = nil, rpms = nil) ⇒ Object
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 |
# File 'lib/simp/rpm.rb', line 455 def self.(project_dir, srpms=nil, rpms=nil) require 'yaml' last_build = { 'git_hash' => %x(git rev-list --max-count=1 HEAD).chomp, 'srpms' => {}, 'rpms' => {} } Dir.chdir(File.join(project_dir, 'dist')) do if srpms.nil? or rpms.nil? all_rpms = Dir.glob('*.rpm') srpms = Dir.glob('src.rpm') rpms = all_rpms - srpms end srpms.each do |srpm| file_stat = File.stat(srpm) last_build['srpms'][File.basename(srpm)] = { 'metadata' => Simp::RPM.get_info(srpm), 'size' => file_stat.size, 'timestamp' => file_stat.ctime, 'path' => File.absolute_path(srpm) } end rpms.each do |rpm| file_stat = File.stat(rpm) last_build['rpms'][File.basename(rpm)] = { 'metadata' => Simp::RPM.get_info(rpm), 'size' => file_stat.size, 'timestamp' => file_stat.ctime, 'path' => File.absolute_path(rpm) } end FileUtils.mkdir_p(File.join(project_dir, 'dist', 'logs')) File.open('logs/last_rpm_build_metadata.yaml','w') do |fh| fh.puts(last_build.to_yaml) end end end |
.execute(cmd) ⇒ Object
Executes a command and returns a hash with the exit status, stdout output and stderr output.
- cmd
-
command to be executed
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
# File 'lib/simp/rpm.rb', line 290 def self.execute(cmd) if @verbose ||= ENV.fetch('SIMP_RPM_verbose','no') =='yes' puts "== Simp::RPM.execute(#{cmd})" puts " #{cmd}" end outfile = File.join('/tmp', "#{ENV['USER']}_#{SecureRandom.hex}") errfile = File.join('/tmp', "#{ENV['USER']}_#{SecureRandom.hex}") pid = spawn(cmd, :out=>outfile, :err=>errfile) begin pid,status = Process.wait2(pid) rescue Errno::ECHILD # process exited before status could be determined end exit_status = status.nil? ? nil : status.exitstatus stdout = IO.read(outfile) stderr = IO.read(errfile) { :exit_status => exit_status, :stdout => stdout, :stderr => stderr } ensure if @verbose puts " -------- exit_status: #{exit_status}" puts " -------- stdout ",'' puts File.readlines(outfile).map{|x| " #{x}"}.join puts ''," -------- stderr ",'' puts File.readlines(errfile).map{|x| " #{x}"}.join end FileUtils.rm_f([outfile, errfile]) end |
.get_info(rpm_source) ⇒ Object
Parses information, such as the version, from the given specfile or RPM into a hash.
If the information from only single RPM is extracted, returns a single Hash with the following possible keys:
:has_dist_tag = a boolean indicating whether the RPM release
has a distribution field; only evaluated when
rpm_source is a spec file, otherwise false
:basename = The name of the package (as it would be
queried in yum)
:version = The version of the package
:release = The release version of the package
:arch = The machine architecture of the package
:full_version = The full version of the package:
<version>-<release>
:name = The full name of the package:
<basename>-<full_version>
:rpm_name = The full name of the RPM:
<basename>-<full_version>.<arch>.rpm
:signature = RPM signature key id; only present if
rpm_source is an RPM and the RPM is signed
If the information from more than one RPM is extracted, as is the case when a spec file specifies sub-packages, returns an Array of Hashes.
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 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/simp/rpm.rb', line 347 def self.get_info(rpm_source) raise "Error: unable to read '#{rpm_source}'" unless File.readable?(rpm_source) if ENV['SIMP_RPM_dist'] target_dist = (ENV['SIMP_RPM_dist'] =~ /^\./) ? ENV['SIMP_RPM_dist'] : ('.' + ENV['SIMP_RPM_dist']) else target_dist = system_dist end info_array = [] common_info = {} rpm_version_query = %Q(#{rpm_cmd} -q --queryformat '%{NAME} %{VERSION} %{RELEASE} %{ARCH}\\n') rpm_signature_query = %Q(#{rpm_cmd} -q --queryformat '%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|\\n') source_is_rpm = rpm_source.split('.').last == 'rpm' if source_is_rpm dist_info = rpm_source.split('-').last.split('.')[1..-3] unless dist_info.empty? common_info[:has_dist_tag] = true common_info[:dist] = '.' + dist_info.first else common_info[:has_dist_tag] = false common_info[:dist] = target_dist end elsif File.read(rpm_source).include?('%{?dist}') common_info[:dist] =target_dist common_info[:has_dist_tag] = true else common_info[:has_dist_tag] = false common_info[:dist] = target_dist end unless source_is_rpm macros = { 'dist' => target_dist } macros.each do |k,v| rpm_version_query += %Q[ -D '#{k} #{v}'] end end if source_is_rpm query_source = "-p #{rpm_source}" version_results = execute("#{rpm_version_query} #{query_source}") signature_results = execute("#{rpm_signature_query} #{query_source}") else query_source = "--specfile #{rpm_source}" version_results = execute("#{rpm_version_query} #{query_source}") signature_results = nil end if version_results[:exit_status] != 0 raise <<~EOE #{indent('Error getting RPM info for #{query_source}:', 2)} #{indent(version_results[:stderr].strip, 5)} #{indent("Run '#{rpm_version_query.gsub("\n",'\\n')} #{query_source}' to recreate the issue.", 2)} EOE end unless signature_results.nil? if signature_results[:exit_status] != 0 raise <<~EOE #{indent('Error getting RPM signature for #{query_source}:', 2)} #{indent(signature_results[:stderr].strip, 5)} #{indent("Run '#{rpm_signature_query.gsub("\n",'\\n')} #{query_source}' to recreate the issue.", 2)} EOE else signature = signature_results[:stdout].strip end end version_results[:stdout].strip.lines.each do |line| info = common_info.dup parts = line.split(' ') info[:basename], info[:version], info[:release], info[:arch] = parts info[:signature] = signature unless signature.nil? or signature.include?('none') info[:full_version] = "#{info[:version]}-#{info[:release]}" info[:name] = "#{info[:basename]}-#{info[:full_version]}" info[:rpm_name] = "#{info[:name]}.#{info[:arch]}.rpm" info_array << info end if @verbose puts "== SIMP::RPM.get_info" require 'pp' pp info_array end if info_array.size == 1 return info_array[0] else # will only happen when source is spec file and that spec file # specifies sub-packages return info_array end end |
.indent(message, indent_length) ⇒ Object
451 452 453 |
# File 'lib/simp/rpm.rb', line 451 def self.indent(, indent_length) .split("\n").map {|line| ' '*indent_length + line }.join("\n") end |
.rpm_cmd ⇒ Object
25 26 27 |
# File 'lib/simp/rpm.rb', line 25 def self.rpm_cmd @rpm_cmd ||= (ENV.fetch('SIMP_RPM_LUA_debug','no') =='yes') ? "rpm -D 'lua_debug 1'" : 'rpm' end |
.sh(args) ⇒ Object
20 21 22 |
# File 'lib/simp/rpm.rb', line 20 def self.sh(args) system args end |
.system_dist ⇒ Object
This causes problems for ISO builds that target a particular OS if it doesn’t match the host. Set the environment variable ‘SIMP_RPM_dist` to ensure all packages use a particular dist.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/simp/rpm.rb', line 77 def self.system_dist # We can only have one of these unless defined?(@@system_dist) cmd = %Q(#{rpm_cmd} -E '%{dist}' 2> /dev/null) if @verbose puts "== Simp::RPM.system_dist" puts " #{cmd} " end dist = %x{#{cmd}}.strip.split('.') puts " result = '#{dist}'" if @verbose if dist.size > 1 @@system_dist = (dist[1] =~ /^\./) ? dist[1] : ('.' + dist[1]) else @@system_dist = nil end puts " @@system_dist = #{@@system_dist ||'nil'}" if @verbose end return @@system_dist end |
.version ⇒ Object
Returns the version of RPM installed on the system
501 502 503 |
# File 'lib/simp/rpm.rb', line 501 def self.version %x{rpm --version}.strip.split.last end |
Instance Method Details
#arch(package = @packages.first) ⇒ Object
184 185 186 187 |
# File 'lib/simp/rpm.rb', line 184 def arch(package=@packages.first) valid_package?(package) @info[package][:arch] end |
#basename(package = @packages.first) ⇒ Object
145 146 147 148 |
# File 'lib/simp/rpm.rb', line 145 def basename(package=@packages.first) valid_package?(package) @info[package][:basename] end |
#dist(package = @packages.first) ⇒ Object
‘dist` of the OS itself. Logic should check both `has_dist_tag?` and `dist`
220 221 222 223 |
# File 'lib/simp/rpm.rb', line 220 def dist(package=@packages.first) valid_package?(package) @info[package][:dist] end |
#full_version(package = @packages.first) ⇒ Object
169 170 171 172 |
# File 'lib/simp/rpm.rb', line 169 def full_version(package=@packages.first) valid_package?(package) @info[package][:full_version] end |
#has_dist_tag?(package = @packages.first) ⇒ Boolean
210 211 212 213 |
# File 'lib/simp/rpm.rb', line 210 def has_dist_tag?(package=@packages.first) valid_package?(package) @info[package][:has_dist_tag] end |
#name(package = @packages.first) ⇒ Object
176 177 178 179 |
# File 'lib/simp/rpm.rb', line 176 def name(package=@packages.first) valid_package?(package) @info[package][:name] end |
#newer?(other_rpm) ⇒ Boolean
Returns whether or not the current RPM package is newer than the passed RPM.
Uses the first package in the package list as the current RPM package.
230 231 232 |
# File 'lib/simp/rpm.rb', line 230 def newer?(other_rpm) package_newer?(@packages.first, other_rpm) end |
#package_newer?(package, other_rpm) ⇒ Boolean
Returns whether or not the current RPM sub-package is newer than the passed RPM.
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 |
# File 'lib/simp/rpm.rb', line 236 def package_newer?(package, other_rpm) valid_package?(package) return true if other_rpm.nil? || other_rpm.empty? unless other_rpm.match(%r(\.rpm$)) raise ArgumentError.new("You must pass valid RPM name! Got: '#{other_rpm}'") end if File.readable?(other_rpm) other_full_version = Simp::RPM.get_info(other_rpm)[:full_version] else # determine RPM info in a hacky way, ASSUMING, the other RPM has the # same basename and arch other_full_version = other_rpm.gsub(/#{package}\-/,'').gsub(/.rpm$/,'') package_arch = arch(package) unless package_arch.nil? or package_arch.empty? other_full_version.gsub!(/.#{package_arch}/,'') end end begin return Gem::Version.new(full_version(package)) > Gem::Version.new(other_full_version) rescue ArgumentError, NoMethodError fail("Could not compare RPMs '#{rpm_name(package)}' and '#{other_rpm}'") end end |
#release(package = @packages.first) ⇒ Object
161 162 163 164 |
# File 'lib/simp/rpm.rb', line 161 def release(package=@packages.first) valid_package?(package) @info[package][:release] end |
#rpm_name(package = @packages.first) ⇒ Object
202 203 204 205 |
# File 'lib/simp/rpm.rb', line 202 def rpm_name(package=@packages.first) valid_package?(package) @info[package][:rpm_name] end |
#signature(package = @packages.first) ⇒ Object
194 195 196 197 |
# File 'lib/simp/rpm.rb', line 194 def signature(package=@packages.first) valid_package?(package) @info[package][:signature] end |
#system_dist ⇒ Object
99 100 101 |
# File 'lib/simp/rpm.rb', line 99 def system_dist return Simp::RPM.system_dist end |
#update_rpmmacros ⇒ Object
Work around the silliness with ‘centos’ being tacked onto things via the ‘dist’ flag
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 |
# File 'lib/simp/rpm.rb', line 105 def update_rpmmacros unless defined?(@@macros_updated) # Workaround for CentOS system builds dist = ENV['SIMP_RPM_dist'] || system_dist dist_macro = %(%dist #{dist}) rpmmacros = [dist_macro] rpmmacros_file = File.join(ENV['HOME'], '.rpmmacros') if File.exist?(rpmmacros_file) rpmmacros = File.read(rpmmacros_file).split("\n") dist_index = rpmmacros.each_index.select{|i| rpmmacros[i] =~ /^%dist\s+/}.first if dist_index rpmmacros[dist_index] = dist_macro else rpmmacros << dist_macro end end File.open(rpmmacros_file, 'w') do |fh| fh.puts rpmmacros.join("\n") fh.flush end if @verbose puts "== SIMP::RPM#update_rpmmacros:" puts " wrote to '#{rpmmacros_file}': " puts " #{'-'*20}" puts rpmmacros.map{|x| " #{x}\n"}.join puts end @@macros_updated = true end end |
#valid_package?(package) ⇒ Boolean
265 266 267 268 269 |
# File 'lib/simp/rpm.rb', line 265 def valid_package?(package) unless @packages.include?(package) raise ArgumentError.new("'#{package}' is not a valid sub-package") end end |
#version(package = @packages.first) ⇒ Object
153 154 155 156 |
# File 'lib/simp/rpm.rb', line 153 def version(package=@packages.first) valid_package?(package) @info[package][:version] end |