simp-rake-helpers 5.11.2 → 5.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -0
  3. data/CONTRIBUTING.md +1 -1
  4. data/Gemfile +5 -1
  5. data/README.md +1 -1
  6. data/lib/simp/command_utils.rb +21 -0
  7. data/lib/simp/componentinfo.rb +17 -0
  8. data/lib/simp/local_gpg_signing_key.rb +184 -81
  9. data/lib/simp/rake.rb +3 -10
  10. data/lib/simp/rake/build/build.rb +43 -27
  11. data/lib/simp/rake/build/constants.rb +5 -1
  12. data/lib/simp/rake/build/pkg.rb +167 -51
  13. data/lib/simp/rake/build/tar.rb +1 -1
  14. data/lib/simp/rake/helpers/version.rb +1 -1
  15. data/lib/simp/rake/pkg.rb +5 -1
  16. data/lib/simp/rake/pupmod/helpers.rb +2 -0
  17. data/lib/simp/rake/rubygem.rb +5 -1
  18. data/lib/simp/relchecks.rb +1 -1
  19. data/lib/simp/rpm.rb +13 -125
  20. data/lib/simp/rpm_signer.rb +321 -0
  21. data/spec/acceptance/00_pkg_rpm_custom_scriptlets_spec.rb +18 -19
  22. data/spec/acceptance/10_pkg_rpm_spec.rb +46 -48
  23. data/spec/acceptance/50_local_gpg_signing_key_spec.rb +7 -3
  24. data/spec/acceptance/55_build_pkg_signing_spec.rb +293 -42
  25. data/spec/acceptance/files/testpackage/README +8 -0
  26. data/spec/acceptance/files/testpackage/spec/classes/init_spec.rb +1 -0
  27. data/spec/acceptance/files/testpackage/spec/files/mock_something.rb +3 -0
  28. data/spec/acceptance/files/testpackage/utils/convert_v1_to_v2.rb +3 -0
  29. data/spec/acceptance/nodesets/default.yml +34 -109
  30. data/spec/acceptance/support/build_project_helpers.rb +32 -8
  31. data/spec/lib/simp/ci/gitlab_spec.rb +12 -13
  32. data/spec/lib/simp/command_utils_spec.rb +29 -0
  33. data/spec/lib/simp/componentinfo_spec.rb +10 -4
  34. data/spec/lib/simp/local_gpg_signing_key_spec.rb.beaker-only +115 -18
  35. data/spec/lib/simp/rake/build/helpers_spec.rb +3 -0
  36. data/spec/lib/simp/rake/build/rpmdeps_spec.rb +1 -2
  37. data/spec/lib/simp/rake/pupmod/fixtures/othermod/Gemfile +1 -10
  38. data/spec/lib/simp/rake/pupmod/fixtures/simpmod/README.md +2 -2
  39. data/spec/lib/simp/rake_spec.rb +2 -1
  40. data/spec/lib/simp/relchecks_check_rpm_changelog_spec.rb +20 -10
  41. data/spec/lib/simp/relchecks_compare_latest_tag_spec.rb +18 -18
  42. data/spec/lib/simp/rpm_signer_spec.rb +98 -0
  43. data/spec/lib/simp/rpm_spec.rb +1 -7
  44. data/spec/spec_helper.rb +1 -1
  45. data/spec/spec_helper_acceptance.rb +16 -3
  46. metadata +13 -69
  47. data/.travis.yml +0 -60
  48. data/spec/acceptance/20_pkg_rpm_upgrade_spec.rb +0 -236
  49. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-2.1/CHANGELOG +0 -2
  50. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-2.1/Rakefile +0 -3
  51. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-2.1/build/rpm_metadata/custom/overrides +0 -14
  52. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-2.1/build/rpm_metadata/requires +0 -1
  53. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-2.1/metadata.json +0 -33
  54. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-3.0/CHANGELOG +0 -2
  55. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-3.0/Rakefile +0 -3
  56. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-3.0/build/rpm_metadata/custom/overrides +0 -14
  57. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-3.0/build/rpm_metadata/requires +0 -1
  58. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-3.0/metadata.json +0 -33
  59. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-1.0/CHANGELOG +0 -2
  60. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-1.0/Rakefile +0 -3
  61. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-1.0/build/rpm_metadata/requires +0 -1
  62. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-1.0/metadata.json +0 -33
  63. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.0/CHANGELOG +0 -2
  64. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.0/Rakefile +0 -3
  65. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.0/build/rpm_metadata/requires +0 -1
  66. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.0/metadata.json +0 -33
  67. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.2/CHANGELOG +0 -2
  68. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.2/Rakefile +0 -3
  69. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.2/build/rpm_metadata/custom/overrides +0 -14
  70. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.2/build/rpm_metadata/requires +0 -1
  71. data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.2/metadata.json +0 -33
  72. data/spec/acceptance/files/mock_packages/pupmod-puppetlabs-stdlib.spec +0 -32
  73. data/spec/acceptance/files/mock_packages/pupmod-simp-foo.spec +0 -32
  74. data/spec/acceptance/files/mock_packages/pupmod-simp-simplib.spec +0 -32
  75. data/spec/acceptance/files/mock_packages/rpmbuild.sh +0 -25
  76. data/spec/acceptance/files/mock_packages/simp-adapter.spec +0 -43
  77. data/spec/acceptance/files/mock_packages/simp-adapter/etc/simp/adapter_config.yaml +0 -3
  78. data/spec/acceptance/files/mock_packages/simp-adapter/usr/local/sbin/simp_rpm_helper +0 -495
  79. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/CHANGELOG +0 -2
  80. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/Rakefile +0 -3
  81. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/build/rpm_metadata/requires +0 -2
  82. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/data/os/CentOS.yaml +0 -2
  83. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/data/os/RedHat.yaml +0 -2
  84. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/hiera.yaml +0 -14
  85. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/manifests/init.pp +0 -2
  86. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/metadata.json +0 -37
  87. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/CHANGELOG +0 -5
  88. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/Rakefile +0 -3
  89. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/build/rpm_metadata/requires +0 -2
  90. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/data/os/CentOS.yaml +0 -2
  91. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/data/os/RedHat.yaml +0 -2
  92. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/hiera.yaml +0 -14
  93. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/manifests/init.pp +0 -3
  94. data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/metadata.json +0 -37
  95. data/spec/lib/simp/ci/files/job_broken_link_nodeset/spec/acceptance/suites/default/nodesets +0 -1
  96. data/spec/lib/simp/ci/files/job_invalid_nodeset/spec/acceptance/suites/default/nodesets +0 -1
  97. data/spec/lib/simp/ci/files/job_invalid_suite/spec/acceptance/suites/default/nodesets +0 -1
  98. data/spec/lib/simp/ci/files/job_missing_nodeset/spec/acceptance/suites/default/nodesets +0 -1
  99. data/spec/lib/simp/ci/files/job_missing_suite_and_nodeset/spec/acceptance/suites/default/nodesets +0 -1
  100. data/spec/lib/simp/ci/files/multiple_invalid_jobs/spec/acceptance/suites/default/nodesets +0 -1
  101. data/spec/lib/simp/ci/files/multiple_valid_jobs/spec/acceptance/suites/default/nodesets +0 -1
  102. data/spec/lib/simp/ci/files/no_gitlab_config_with_tests/spec/acceptance/suites/default/nodesets +0 -1
  103. data/spec/lib/simp/ci/files/no_gitlab_config_without_tests/spec/acceptance/suites/default/nodesets +0 -1
  104. data/spec/lib/simp/ci/files/suite_skeleton_only/spec/acceptance/nodesets/default.yml +0 -1
  105. data/spec/lib/simp/ci/files/suite_skeleton_only/spec/acceptance/suites/default/nodesets +0 -1
  106. data/spec/lib/simp/ci/files/valid_job_nodeset_dir_link/spec/acceptance/suites/default/nodesets +0 -1
  107. data/spec/lib/simp/ci/files/valid_job_nodeset_link/spec/acceptance/suites/default/nodesets/default.yml +0 -1
  108. data/spec/lib/simp/files/build/testpackage.spec +0 -1
  109. data/spec/lib/simp/rake/pupmod/fixtures/simpmod/spec/acceptance/nodesets/default.yml +0 -1
  110. data/spec/lib/simp/rake/pupmod/fixtures/simpmod/spec/acceptance/suites/default/nodesets +0 -1
@@ -62,7 +62,7 @@ module Simp::Rake::Build
62
62
  else
63
63
  required_rpms['noarch'] << 'simp-rsync-skeleton'
64
64
  required_rpms['noarch'] << 'simp-environment-skeleton'
65
- required_rpms['noarch'] << 'simp-environment-selinux-policy'
65
+ required_rpms['noarch'] << 'simp-selinux-policy'
66
66
  end
67
67
 
68
68
  rpm_dir = File.join(@build_dir,'SIMP','RPMS')
@@ -2,5 +2,5 @@ module Simp; end
2
2
  module Simp::Rake; end
3
3
 
4
4
  class Simp::Rake::Helpers
5
- VERSION = '5.11.2'
5
+ VERSION = '5.12.0'
6
6
  end
data/lib/simp/rake/pkg.rb CHANGED
@@ -235,7 +235,11 @@ module Simp::Rake
235
235
  %(-D '_sourcedir #{@rpm_srcdir}'),
236
236
  %(-D '_rpmdir #{@pkg_dir}'),
237
237
  %(-D '_srcrpmdir #{@pkg_dir}'),
238
- %(-D '_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm')
238
+ %(-D '_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm'),
239
+
240
+ # needed on EL8 to disable the aggressive brp_mangle_shebangs script
241
+ # that results in invalid script shebangs; does nothing in EL7
242
+ %(-D '__brp_mangle_shebangs /usr/bin/true')
239
243
  ]
240
244
  rpm_opts << '-v' if @verbose
241
245
  rpm_opts << "-D 'lua_debug 1'" if (ENV.fetch('SIMP_RAKE_PKG_LUA_verbose','no') =='yes')
@@ -290,6 +290,7 @@ class Simp::Rake::Pupmod::Helpers < ::Rake::TaskLib
290
290
  # That will give Travis a way of warning us if the changelog
291
291
  # will prevent the rpm from building.
292
292
  task :changelog_annotation, [:quiet] do |t,args|
293
+ warning('DEPRECATED: use pkg:create_tag_changelog')
293
294
  quiet = true if args[:quiet].to_s == 'true'
294
295
  puts changelog_annotation( quiet )
295
296
  end
@@ -332,6 +333,7 @@ class Simp::Rake::Pupmod::Helpers < ::Rake::TaskLib
332
333
  - doc directory
333
334
  EOM
334
335
  task :compare_latest_tag, [:tags_source, :ignore_owner, :verbose] do |t,args|
336
+ warning('DEPRECATED: use pkg:compare_latest_tag')
335
337
  require 'json'
336
338
 
337
339
  tags_source = args[:tags_source].nil? ? 'origin' : args[:tags_source]
@@ -49,7 +49,11 @@ module Simp::Rake
49
49
  task :install_gem => [:clean, :gem] do
50
50
  Dir.chdir @rakefile_dir
51
51
  Dir.glob("dist/#{@package}*.gem") do |pkg|
52
- sh %Q{bundle exec gem install --no-ri --no-rdoc #{pkg}}
52
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6')
53
+ sh %Q{bundle exec gem install --no-document #{pkg}}
54
+ else
55
+ sh %Q{bundle exec gem install --no-ri --no-rdoc #{pkg}}
56
+ end
53
57
  end
54
58
  end
55
59
  end
@@ -87,7 +87,7 @@ class Simp::RelChecks
87
87
  # determine mission-impacting files that have changed
88
88
  files_changed = `git diff tags/#{last_tag} --name-only`.strip.split("\n")
89
89
  files_changed.delete_if do |file|
90
- file[0] == '.' or file == 'Rakefile' or file =~ /^Gemfile|^spec\/|^doc\/|^rakelib\//
90
+ file[0] == '.' or file == 'Rakefile' or file =~ /^Gemfile|^spec\/|^doc\/|^rakelib\/|.*\.md\Z/
91
91
  end
92
92
 
93
93
  if files_changed.empty?
data/lib/simp/rpm.rb CHANGED
@@ -14,7 +14,6 @@ module Simp
14
14
  require 'pty'
15
15
  require 'rake'
16
16
 
17
- @@gpg_keys = Hash.new
18
17
  attr_reader :verbose, :lua_debug, :packages
19
18
 
20
19
  if Gem.loaded_specs['rake'].version >= Gem::Version.new('0.9')
@@ -402,20 +401,20 @@ module Simp
402
401
  end
403
402
 
404
403
  if version_results[:exit_status] != 0
405
- raise <<-EOE
406
- #{indent('Error getting RPM info:', 2)}
407
- #{indent(version_results[:stderr].strip, 5)}
408
- #{indent("Run '#{rpm_version_query.gsub("\n",'\\n')} #{query_source}' to recreate the issue.", 2)}
409
- EOE
404
+ raise <<~EOE
405
+ #{indent('Error getting RPM info for #{query_source}:', 2)}
406
+ #{indent(version_results[:stderr].strip, 5)}
407
+ #{indent("Run '#{rpm_version_query.gsub("\n",'\\n')} #{query_source}' to recreate the issue.", 2)}
408
+ EOE
410
409
  end
411
410
 
412
411
  unless signature_results.nil?
413
412
  if signature_results[:exit_status] != 0
414
- raise <<-EOE
415
- #{indent('Error getting RPM signature:', 2)}
416
- #{indent(signature_results[:stderr].strip, 5)}
417
- #{indent("Run '#{rpm_signature_query.gsub("\n",'\\n')} #{query_source}' to recreate the issue.", 2)}
418
- EOE
413
+ raise <<~EOE
414
+ #{indent('Error getting RPM signature for #{query_source}:', 2)}
415
+ #{indent(signature_results[:stderr].strip, 5)}
416
+ #{indent("Run '#{rpm_signature_query.gsub("\n",'\\n')} #{query_source}' to recreate the issue.", 2)}
417
+ EOE
419
418
  else
420
419
  signature = signature_results[:stdout].strip
421
420
  end
@@ -498,120 +497,9 @@ EOE
498
497
  end
499
498
  end
500
499
 
501
- # Loads metadata for a GPG key. The GPG key is to be used to sign RPMs. The
502
- # value of gpg_key should be the full path of the directory where the key
503
- # resides. If the metadata cannot be found, then the user will be prompted
504
- # for it.
505
- def self.load_key(gpg_key)
506
- keydir = gpg_key
507
- File.directory?(keydir) || fail("Error: Could not find '#{keydir}'")
508
-
509
- gpg_key = File.basename(gpg_key)
510
-
511
- if @@gpg_keys[gpg_key]
512
- return @@gpg_keys[gpg_key]
513
- end
514
-
515
- gpg_name = nil
516
- gpg_password = nil
517
- begin
518
- File.read("#{keydir}/gengpgkey").each_line do |ln|
519
- name_line = ln.split(/^\s*Name-Email:/)
520
- if name_line.length > 1
521
- gpg_name = name_line.last.strip
522
- end
523
-
524
- passwd_line = ln.split(/^\s*Passphrase:/)
525
- if passwd_line.length > 1
526
- gpg_password = passwd_line.last.strip
527
- end
528
- end
529
- rescue Errno::ENOENT
530
- end
531
-
532
- if gpg_name.nil?
533
- puts "Warning: Could not find valid e-mail address for use with GPG."
534
- puts "Please enter e-mail address to use:"
535
- gpg_name = $stdin.gets.strip
536
- end
537
-
538
- if gpg_password.nil?
539
- if File.exist?(%(#{keydir}/password))
540
- gpg_password = File.read(%(#{keydir}/password)).chomp
541
- end
542
-
543
- if gpg_password.nil?
544
- puts "Warning: Could not find a password in '#{keydir}/password'!"
545
- puts "Please enter your GPG key password:"
546
- system 'stty -echo'
547
- gpg_password = $stdin.gets.strip
548
- system 'stty echo'
549
- end
550
- end
551
-
552
- gpg_key_size = nil
553
- gpg_key_id = nil
554
- %x(gpg --homedir=#{keydir} --list-keys #{gpg_name} 2>&1).each_line do |line|
555
- head,data = line.split(/\s+/)
556
- if head == 'pub'
557
- gpg_key_size,gpg_key_id = data.split('/')
558
- break
559
- end
560
- end
561
-
562
- if !gpg_key_size || !gpg_key_id
563
- fail("Error getting GPG Key metadata")
564
- end
565
-
566
- @@gpg_keys[gpg_key] = {
567
- :dir => keydir,
568
- :name => gpg_name,
569
- :key_id => gpg_key_id,
570
- :key_size => gpg_key_size,
571
- :password => gpg_password
572
- }
573
- end
574
-
575
- # Signs the given RPM with the given gpg_key (see Simp::RPM.load_key for
576
- # details on the value of this parameter).
577
- def self.signrpm(rpm, gpg_key)
578
- gpgkey = load_key(gpg_key)
579
-
580
- gpg_sig = nil
581
- %x(rpm -Kv #{rpm}).each_line do |line|
582
- if line =~ /key\sID\s(.*):/
583
- gpg_sig = $1.strip
584
- end
585
- end
586
-
587
- unless gpg_sig == gpgkey[:key_id]
588
- signcommand = "rpm " +
589
- "--define '%_signature gpg' " +
590
- "--define '%__gpg %{_bindir}/gpg' " +
591
- "--define '%_gpg_name #{gpgkey[:name]}' " +
592
- "--define '%_gpg_path #{gpgkey[:dir]}' " +
593
- "--resign #{rpm}"
594
- begin
595
- PTY.spawn(signcommand) do |read, write, pid|
596
- begin
597
- while !read.eof? do
598
- read.expect(/pass\s?phrase:.*/) do |text|
599
- write.puts(gpgkey[:password])
600
- write.flush
601
- end
602
- end
603
- rescue Errno::EIO
604
- # This ALWAYS happens in Ruby 1.8.
605
- end
606
- Process.wait(pid)
607
- end
608
-
609
- raise "Failure running #{signcommand}" unless $?.success?
610
- rescue Exception => e
611
- puts "Error occured while attempting to sign #{rpm}, skipping."
612
- puts e
613
- end
614
- end
500
+ # Returns the version of RPM installed on the system
501
+ def self.version
502
+ %x{rpm --version}.strip.split.last
615
503
  end
616
504
  end
617
505
  end
@@ -0,0 +1,321 @@
1
+ require 'find'
2
+ require 'parallel'
3
+ require 'simp/rpm'
4
+ require 'simp/command_utils'
5
+
6
+ module Simp; end
7
+
8
+ # Class to sign RPMs. Uses 'gpg' and 'rpm' executables.
9
+ class Simp::RpmSigner
10
+ require 'expect'
11
+ require 'pty'
12
+
13
+ extend Simp::CommandUtils
14
+
15
+ @@gpg_keys = Hash.new
16
+
17
+ # Kill the GPG agent operating with the specified key dir, if
18
+ # rpm version 4.13.0 or later.
19
+ #
20
+ # Beginning with version 4.13.0, rpm stands up a gpg-agent when
21
+ # a signing operation is requested.
22
+ def self.kill_gpg_agent(gpg_keydir)
23
+ return if Gem::Version.new(Simp::RPM.version) < Gem::Version.new('4.13.0')
24
+
25
+ %x(gpg-agent --homedir #{gpg_keydir} -q >& /dev/null)
26
+ if $? && ($?.exitstatus == 0)
27
+ # gpg-agent is running for specified keydir, so query it for its pid
28
+ output = %x{echo 'GETINFO pid' | gpg-connect-agent --homedir=#{gpg_keydir}}
29
+ if $? && ($?.exitstatus == 0)
30
+ pid = output.lines.first[1..-1].strip.to_i
31
+ begin
32
+ Process.kill(0, pid)
33
+ Process.kill(15, pid)
34
+ rescue Errno::ESRCH
35
+ # No longer running, so nothing to do!
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ # Loads metadata for a GPG key found in gpg_keydir.
42
+ #
43
+ # The GPG key is to be used to sign RPMs. If the required metadata cannot be
44
+ # retrieved from files found in the gpg_keydir, the user will be prompted
45
+ # for it.
46
+ #
47
+ # @param gpg_keydir The full path of the directory where the key resides
48
+ # @param verbose Whether to log debug information.
49
+ #
50
+ # @raise If the 'gpg' executable cannot be found, the GPG key directory
51
+ # does not exist or GPG key metadata cannot be determined via 'gpg'
52
+ #
53
+ def self.load_key(gpg_keydir, verbose = false)
54
+ which('gpg') || raise("ERROR: Cannot sign RPMs without 'gpg'")
55
+ File.directory?(gpg_keydir) || raise("ERROR: Could not find GPG keydir '#{gpg_keydir}'")
56
+
57
+ gpg_key = File.basename(gpg_keydir)
58
+
59
+ if @@gpg_keys[gpg_key]
60
+ return @@gpg_keys[gpg_key]
61
+ end
62
+
63
+ gpg_name = nil
64
+ gpg_password = nil
65
+ begin
66
+ File.read("#{gpg_keydir}/gengpgkey").each_line do |ln|
67
+ name_line = ln.split(/^\s*Name-Email:/)
68
+ if name_line.length > 1
69
+ gpg_name = name_line.last.strip
70
+ end
71
+
72
+ passwd_line = ln.split(/^\s*Passphrase:/)
73
+ if passwd_line.length > 1
74
+ gpg_password = passwd_line.last.strip
75
+ end
76
+ end
77
+ rescue Errno::ENOENT
78
+ end
79
+
80
+ if gpg_name.nil?
81
+ puts "Warning: Could not find valid e-mail address for use with GPG."
82
+ puts "Please enter e-mail address to use:"
83
+ gpg_name = $stdin.gets.strip
84
+ end
85
+
86
+ if gpg_password.nil?
87
+ if File.exist?(%(#{gpg_keydir}/password))
88
+ gpg_password = File.read(%(#{gpg_keydir}/password)).chomp
89
+ end
90
+
91
+ if gpg_password.nil?
92
+ puts "Warning: Could not find a password in '#{gpg_keydir}/password'!"
93
+ puts "Please enter your GPG key password:"
94
+ system 'stty -echo'
95
+ gpg_password = $stdin.gets.strip
96
+ system 'stty echo'
97
+ end
98
+ end
99
+
100
+ gpg_key_size = nil
101
+ gpg_key_id = nil
102
+ cmd = "gpg --with-colons --homedir=#{gpg_keydir} --list-keys '<#{gpg_name}>' 2>&1"
103
+ puts "Executing: #{cmd}" if verbose
104
+ %x(#{cmd}).each_line do |line|
105
+ # See https://github.com/CSNW/gnupg/blob/master/doc/DETAILS
106
+ # Index Content
107
+ # 0 record type
108
+ # 2 key length
109
+ # 4 keyID
110
+ fields = line.split(':')
111
+ if fields[0] && (fields[0] == 'pub')
112
+ gpg_key_size = fields[2].to_i
113
+ gpg_key_id = fields[4]
114
+ break
115
+ end
116
+ end
117
+
118
+ if !gpg_key_size || !gpg_key_id
119
+ raise("Error getting GPG key ID or Key size metadata for #{gpg_name}")
120
+ end
121
+
122
+ @@gpg_keys[gpg_key] = {
123
+ :dir => gpg_keydir,
124
+ :name => gpg_name,
125
+ :key_id => gpg_key_id,
126
+ :key_size => gpg_key_size,
127
+ :password => gpg_password
128
+ }
129
+ end
130
+
131
+ # Signs the given RPM with the GPG key found in gpg_keydir
132
+ #
133
+ # @param rpm Fully qualified path to an RPM to be signed.
134
+ # @param gpg_keydir The full path of the directory where the key resides.
135
+ # @param options Options Hash
136
+ #
137
+ # @options options :digest_algo Digest algorithm to use in RPM
138
+ # signing operation; defaults to 'sha256'
139
+ # @options options :timeout_seconds Timeout in seconds for an individual
140
+ # RPM signing operation; defaults to 60.
141
+ # @options options :verbose Whether to log debug information;
142
+ # defaults to false.
143
+ #
144
+ # @return Whether package signing operation succeeded
145
+ # @raise RuntimeError if 'rpmsign' executable cannot be found, the 'gpg
146
+ # 'executable cannot be found, the GPG key directory does not exist or
147
+ # the GPG key metadata cannot be determined via 'gpg'
148
+ def self.sign_rpm(rpm, gpg_keydir, options={})
149
+ # This may be a little confusing...Although we're using 'rpm --resign'
150
+ # in lieu of 'rpmsign --addsign', they are equivalent and the presence
151
+ # of 'rpmsign' is a legitimate check that the 'rpm --resign' capability
152
+ # is available (i.e., rpm-sign package has been installed).
153
+ which('rpmsign') || raise("ERROR: Cannot sign RPMs without 'rpmsign'.")
154
+
155
+ digest_algo = options.key?(:digest_algo) ? options[:digest_algo] : 'sha256'
156
+ timeout_seconds = options.key?(:timeout_seconds) ? options[:timeout_seconds] : 60
157
+ verbose = options.key?(:verbose) ? options[:verbose] : false
158
+
159
+ gpgkey = load_key(gpg_keydir, verbose)
160
+
161
+ gpg_sign_cmd_extra_args = nil
162
+ if Gem::Version.new(Simp::RPM.version) >= Gem::Version.new('4.13.0')
163
+ gpg_sign_cmd_extra_args = "--define '%_gpg_sign_cmd_extra_args --pinentry-mode loopback --verbose'"
164
+ end
165
+
166
+ signcommand = [
167
+ 'rpm',
168
+ "--define '%_signature gpg'",
169
+ "--define '%__gpg %{_bindir}/gpg'",
170
+ "--define '%_gpg_name #{gpgkey[:name]}'",
171
+ "--define '%_gpg_path #{gpgkey[:dir]}'",
172
+ "--define '%_gpg_digest_algo #{digest_algo}'",
173
+ gpg_sign_cmd_extra_args,
174
+ "--resign #{rpm}"
175
+ ].compact.join(' ')
176
+
177
+ success = false
178
+ begin
179
+ if verbose
180
+ puts "Signing #{rpm} with #{gpgkey[:name]} from #{gpgkey[:dir]}:\n #{signcommand}"
181
+ end
182
+
183
+ require 'timeout'
184
+ # With rpm-sign-4.14.2-11.el8_0 (EL 8.0), if rpm cannot start the
185
+ # gpg-agent daemon, it will just hang. We need to be able to detect
186
+ # the problem and report the failure.
187
+ Timeout::timeout(timeout_seconds) do
188
+
189
+ status = nil
190
+ PTY.spawn(signcommand) do |read, write, pid|
191
+ begin
192
+ while !read.eof? do
193
+ # rpm version >= 4.13.0 will stand up a gpg-agent and so the
194
+ # prompt for the passphrase will only actually happen if this is
195
+ # the first RPM to be signed with the key after the gpg-agent is
196
+ # started and the key's passphrase has not been cleared from the
197
+ # agent's cache.
198
+ read.expect(/pass\s?phrase:.*/) do |text|
199
+ write.puts(gpgkey[:password])
200
+ write.flush
201
+ end
202
+ end
203
+ rescue Errno::EIO
204
+ # Will get here once input is no longer needed, which can be
205
+ # immediately, if a gpg-agent is already running and the
206
+ # passphrase for the key is loaded in its cache.
207
+ end
208
+
209
+ Process.wait(pid)
210
+ status = $?
211
+ end
212
+
213
+ if status && !status.success?
214
+ raise "Failure running <#{signcommand}>"
215
+ end
216
+ end
217
+
218
+ puts "Successfully signed #{rpm}" if verbose
219
+ success = true
220
+
221
+ rescue Timeout::Error
222
+ $stderr.puts "Failed to sign #{rpm} in #{timeout_seconds} seconds."
223
+ rescue Exception => e
224
+ $stderr.puts "Error occurred while attempting to sign #{rpm}:"
225
+ $stderr.puts e
226
+ end
227
+
228
+ success
229
+ end
230
+
231
+ # Signs any RPMs found within the entire rpm_dir directory tree with
232
+ # the GPG key found in gpg_keydir
233
+ #
234
+ # @param rpm_dir A directory or directory glob pattern specifying 1 or
235
+ # more directories containing RPM files to sign.
236
+ # @param gpg_keydir The full path of the directory where the key resides
237
+ # @param options Options Hash
238
+ #
239
+ # @options options :digest_algo Digest algorithm to use in RPM
240
+ # signing operation; defaults to
241
+ # 'sha256'
242
+ # @options options :force Force RPMs that are already signed
243
+ # to be resigned; defaults to false.
244
+ # @options options :max_concurrent Maximum number of concurrent RPM
245
+ # signing operations; defaults to 1.
246
+ # @options options :progress_bar_title Title for the progress bar logged to
247
+ # the console during the signing process;
248
+ # defaults to 'sign_rpms'.
249
+ # @options options :timeout_seconds Timeout in seconds for an individual
250
+ # RPM signing operation; defauls to 60.
251
+ # @options options :verbose Whether to log debug information;
252
+ # defaults to false.
253
+ #
254
+ # @return Hash of RPM signing results or nil if no RPMs found in rpm_dir
255
+ # * Each Hash key is the path to a RPM
256
+ # * Each Hash value is the status of the signing operation: :signed,
257
+ # :unsigned, :skipped_already_signed
258
+ #
259
+ # @raise RuntimeError if 'rpmsign' executable cannot be found, the 'gpg'
260
+ # executable cannot be found, the GPG key directory does not exist,
261
+ # the GPG key metadata cannot be determined via 'gpg' or any RPM signing
262
+ # operation failed
263
+ #
264
+ def self.sign_rpms(rpm_dir, gpg_keydir, options = {})
265
+ opts = {
266
+ :digest_algo => 'sha256',
267
+ :force => false,
268
+ :max_concurrent => 1,
269
+ :progress_bar_title => 'sign_rpms',
270
+ :timeout_seconds => 60,
271
+ :verbose => false
272
+ }.merge(options)
273
+
274
+ rpm_dirs = Dir.glob(rpm_dir)
275
+ to_sign = []
276
+
277
+ rpm_dirs.each do |rpm_dir|
278
+ Find.find(rpm_dir) do |rpm|
279
+ next unless File.readable?(rpm)
280
+ to_sign << rpm if rpm =~ /\.rpm$/
281
+ end
282
+ end
283
+
284
+ return nil if to_sign.empty?
285
+
286
+ results = []
287
+ begin
288
+ results = Parallel.map(
289
+ to_sign,
290
+ :in_processes => opts[:max_concurrent],
291
+ :progress => opts[:progress_bar_title]
292
+ ) do |rpm|
293
+ _result = nil
294
+
295
+ begin
296
+ if opts[:force] || !Simp::RPM.new(rpm).signature
297
+ _result = [ rpm, sign_rpm(rpm, gpg_keydir, opts) ]
298
+ _result[1] = _result[1] ? :signed : :unsigned
299
+ else
300
+ puts "Skipping signed package #{rpm}" if opts[:verbose]
301
+ _result = [ rpm, :skipped_already_signed ]
302
+ end
303
+ rescue Exception => e
304
+ # can get here if rpm is malformed and Simp::RPM.new fails
305
+ $stderr.puts "Failed to sign #{rpm}:\n#{e.message}"
306
+ _result = [ rpm, :unsigned ]
307
+ end
308
+
309
+ _result
310
+ end
311
+ ensure
312
+ kill_gpg_agent(gpg_keydir)
313
+ end
314
+
315
+ results.to_h
316
+ end
317
+
318
+ def self.clear_gpg_keys_cache
319
+ @@gpg_keys.clear
320
+ end
321
+ end