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
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/simp/rpm.rb', line 48 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 return unless @verbose require 'pp' puts '== Simp::RPM @packages' puts @packages.pretty_inspect end |
Instance Attribute Details
#lua_debug ⇒ Object (readonly)
Returns the value of attribute lua_debug.
19 20 21 |
# File 'lib/simp/rpm.rb', line 19 def lua_debug @lua_debug end |
#packages ⇒ Object (readonly)
Returns the value of attribute packages.
19 20 21 |
# File 'lib/simp/rpm.rb', line 19 def packages @packages end |
#verbose ⇒ Object (readonly)
Returns the value of attribute verbose.
19 20 21 |
# File 'lib/simp/rpm.rb', line 19 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
274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/simp/rpm.rb', line 274 def self.copy_wo_vcs(start_dir, src, dest, dereference = true) dereference = if dereference.nil? || dereference '--dereference' else '' 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
451 452 453 454 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 |
# File 'lib/simp/rpm.rb', line 451 def self.(project_dir, srpms = nil, rpms = nil) require 'yaml' last_build = { 'git_hash' => `git rev-list --max-count=1 HEAD`.chomp, 'srpms' => {}, 'rpms' => {} } Dir.chdir(File.join(project_dir, 'dist')) do if srpms.nil? || 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
289 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 |
# File 'lib/simp/rpm.rb', line 289 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.fetch('USER', nil)}_#{SecureRandom.hex}") errfile = File.join('/tmp', "#{ENV.fetch('USER', nil)}_#{SecureRandom.hex}") pid = spawn(cmd, :out => outfile, :err => errfile) begin _, status = Process.wait2(pid) rescue Errno::ECHILD # process exited before status could be determined end exit_status = status&.exitstatus stdout = File.read(outfile) stderr = File.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.
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 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 |
# File 'lib/simp/rpm.rb', line 346 def self.get_info(rpm_source) raise "Error: unable to read '#{rpm_source}'" unless File.readable?(rpm_source) target_dist = if ENV['SIMP_RPM_dist'] (ENV.fetch('SIMP_RPM_dist', nil) =~ %r{^\.}) ? ENV.fetch('SIMP_RPM_dist', nil) : ('.' + ENV.fetch('SIMP_RPM_dist', nil)) else system_dist end info_array = [] common_info = {} rpm_version_query = %(#{rpm_cmd} -q --queryformat '%{NAME} %{VERSION} %{RELEASE} %{ARCH}\\n') rpm_signature_query = %(#{rpm_cmd} -q --queryformat '%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|\\n') # rubocop:disable Layout/LineLength source_is_rpm = rpm_source.split('.').last == 'rpm' if source_is_rpm dist_info = rpm_source.split('-').last.split('.')[1..-3] if dist_info.empty? common_info[:has_dist_tag] = false common_info[:dist] = target_dist else common_info[:has_dist_tag] = true common_info[:dist] = '.' + dist_info.first 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 += %( -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? raise <<~EOE if signature_results[:exit_status] != 0 #{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 signature = signature_results[:stdout].strip 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? || 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 info_array[0] else # will only happen when source is spec file and that spec file # specifies sub-packages info_array end end |
.indent(message, indent_length) ⇒ Object
447 448 449 |
# File 'lib/simp/rpm.rb', line 447 def self.indent(, indent_length) .split("\n").map { |line| (' ' * indent_length) + line }.join("\n") end |
.rpm_cmd ⇒ Object
27 28 29 |
# File 'lib/simp/rpm.rb', line 27 def self.rpm_cmd @rpm_cmd ||= (ENV.fetch('SIMP_RPM_LUA_debug', 'no') == 'yes') ? "rpm -D 'lua_debug 1'" : 'rpm' end |
.sh(args) ⇒ Object
22 23 24 |
# File 'lib/simp/rpm.rb', line 22 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.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/simp/rpm.rb', line 79 def self.system_dist # We can only have one of these unless defined?(@@system_dist) cmd = %(#{rpm_cmd} -E '%{dist}' 2> /dev/null) if @verbose puts '== Simp::RPM.system_dist' puts " #{cmd} " end dist = `#{cmd}`.strip.split('.') puts " result = '#{dist}'" if @verbose @@system_dist = if dist.size > 1 (dist[1] =~ %r{^\.}) ? dist[1] : ('.' + dist[1]) else nil end puts " @@system_dist = #{@@system_dist || 'nil'}" if @verbose end @@system_dist end |
.version ⇒ Object
Returns the version of RPM installed on the system
497 498 499 |
# File 'lib/simp/rpm.rb', line 497 def self.version `rpm --version`.strip.split.last end |
Instance Method Details
#arch(package = @packages.first) ⇒ Object
185 186 187 188 |
# File 'lib/simp/rpm.rb', line 185 def arch(package = @packages.first) valid_package?(package) @info[package][:arch] end |
#basename(package = @packages.first) ⇒ Object
146 147 148 149 |
# File 'lib/simp/rpm.rb', line 146 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`
221 222 223 224 |
# File 'lib/simp/rpm.rb', line 221 def dist(package = @packages.first) valid_package?(package) @info[package][:dist] end |
#full_version(package = @packages.first) ⇒ Object
170 171 172 173 |
# File 'lib/simp/rpm.rb', line 170 def full_version(package = @packages.first) valid_package?(package) @info[package][:full_version] end |
#has_dist_tag?(package = @packages.first) ⇒ Boolean
211 212 213 214 |
# File 'lib/simp/rpm.rb', line 211 def has_dist_tag?(package = @packages.first) valid_package?(package) @info[package][:has_dist_tag] end |
#name(package = @packages.first) ⇒ Object
177 178 179 180 |
# File 'lib/simp/rpm.rb', line 177 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.
231 232 233 |
# File 'lib/simp/rpm.rb', line 231 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.
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 |
# File 'lib/simp/rpm.rb', line 237 def package_newer?(package, other_rpm) valid_package?(package) return true if other_rpm.nil? || other_rpm.empty? unless %r{\.rpm$}.match?(other_rpm) raise ArgumentError, "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(%r{#{package}-}, '').gsub(%r{.rpm$}, '') package_arch = arch(package) unless package_arch.nil? || package_arch.empty? other_full_version.gsub!(%r{.#{package_arch}}, '') end end begin Gem::Version.new(full_version(package)) > Gem::Version.new(other_full_version) rescue ArgumentError, NoMethodError raise("Could not compare RPMs '#{rpm_name(package)}' and '#{other_rpm}'") end end |
#release(package = @packages.first) ⇒ Object
162 163 164 165 |
# File 'lib/simp/rpm.rb', line 162 def release(package = @packages.first) valid_package?(package) @info[package][:release] end |
#rpm_name(package = @packages.first) ⇒ Object
203 204 205 206 |
# File 'lib/simp/rpm.rb', line 203 def rpm_name(package = @packages.first) valid_package?(package) @info[package][:rpm_name] end |
#signature(package = @packages.first) ⇒ Object
195 196 197 198 |
# File 'lib/simp/rpm.rb', line 195 def signature(package = @packages.first) valid_package?(package) @info[package][:signature] end |
#system_dist ⇒ Object
101 102 103 |
# File 'lib/simp/rpm.rb', line 101 def system_dist Simp::RPM.system_dist end |
#update_rpmmacros ⇒ Object
Work around the silliness with ‘centos’ being tacked onto things via the ‘dist’ flag
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 141 |
# File 'lib/simp/rpm.rb', line 107 def update_rpmmacros return if 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(Dir.home, '.rpmmacros') if File.exist?(rpmmacros_file) rpmmacros = File.read(rpmmacros_file).split("\n") dist_index = rpmmacros.each_index.find { |i| rpmmacros[i] =~ %r{^%dist\s+} } 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 |
#valid_package?(package) ⇒ Boolean
264 265 266 267 268 |
# File 'lib/simp/rpm.rb', line 264 def valid_package?(package) return false if @packages.include?(package) raise ArgumentError, "'#{package}' is not a valid sub-package" end |
#version(package = @packages.first) ⇒ Object
154 155 156 157 |
# File 'lib/simp/rpm.rb', line 154 def version(package = @packages.first) valid_package?(package) @info[package][:version] end |