simp-rake-helpers 5.11.6 → 5.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +1 -1
- data/lib/simp/command_utils.rb +21 -0
- data/lib/simp/local_gpg_signing_key.rb +128 -79
- data/lib/simp/rake.rb +3 -17
- data/lib/simp/rake/build/pkg.rb +102 -40
- data/lib/simp/rake/helpers/version.rb +1 -1
- data/lib/simp/rake/pkg.rb +5 -1
- data/lib/simp/rake/pupmod/helpers.rb +2 -0
- data/lib/simp/rake/rubygem.rb +5 -1
- data/lib/simp/rpm.rb +10 -127
- data/lib/simp/rpm_signer.rb +321 -0
- data/spec/acceptance/00_pkg_rpm_custom_scriptlets_spec.rb +18 -19
- data/spec/acceptance/10_pkg_rpm_spec.rb +46 -48
- data/spec/acceptance/50_local_gpg_signing_key_spec.rb +7 -3
- data/spec/acceptance/55_build_pkg_signing_spec.rb +293 -42
- data/spec/acceptance/files/testpackage/README +8 -0
- data/spec/acceptance/files/testpackage/spec/classes/init_spec.rb +1 -0
- data/spec/acceptance/files/testpackage/spec/files/mock_something.rb +3 -0
- data/spec/acceptance/files/testpackage/utils/convert_v1_to_v2.rb +3 -0
- data/spec/acceptance/nodesets/default.yml +15 -2
- data/spec/acceptance/support/build_project_helpers.rb +32 -8
- data/spec/lib/simp/command_utils_spec.rb +29 -0
- data/spec/lib/simp/local_gpg_signing_key_spec.rb.beaker-only +115 -18
- data/spec/lib/simp/rake/pupmod/fixtures/simpmod/README.md +2 -2
- data/spec/lib/simp/rpm_signer_spec.rb +98 -0
- data/spec/lib/simp/rpm_spec.rb +0 -6
- metadata +12 -67
- data/.travis.yml +0 -41
- data/spec/acceptance/20_pkg_rpm_upgrade_spec.rb +0 -236
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-2.1/CHANGELOG +0 -2
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-2.1/Rakefile +0 -3
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-2.1/build/rpm_metadata/custom/overrides +0 -14
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-2.1/build/rpm_metadata/requires +0 -1
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-2.1/metadata.json +0 -33
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-3.0/CHANGELOG +0 -2
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-3.0/Rakefile +0 -3
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-3.0/build/rpm_metadata/custom/overrides +0 -14
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-3.0/build/rpm_metadata/requires +0 -1
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-new-package-3.0/metadata.json +0 -33
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-1.0/CHANGELOG +0 -2
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-1.0/Rakefile +0 -3
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-1.0/build/rpm_metadata/requires +0 -1
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-1.0/metadata.json +0 -33
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.0/CHANGELOG +0 -2
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.0/Rakefile +0 -3
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.0/build/rpm_metadata/requires +0 -1
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.0/metadata.json +0 -33
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.2/CHANGELOG +0 -2
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.2/Rakefile +0 -3
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.2/build/rpm_metadata/custom/overrides +0 -14
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.2/build/rpm_metadata/requires +0 -1
- data/spec/acceptance/files/custom_scriptlet_triggers/pupmod-old-package-2.2/metadata.json +0 -33
- data/spec/acceptance/files/mock_packages/pupmod-puppetlabs-stdlib.spec +0 -32
- data/spec/acceptance/files/mock_packages/pupmod-simp-foo.spec +0 -32
- data/spec/acceptance/files/mock_packages/pupmod-simp-simplib.spec +0 -32
- data/spec/acceptance/files/mock_packages/rpmbuild.sh +0 -25
- data/spec/acceptance/files/mock_packages/simp-adapter.spec +0 -43
- data/spec/acceptance/files/mock_packages/simp-adapter/etc/simp/adapter_config.yaml +0 -3
- data/spec/acceptance/files/mock_packages/simp-adapter/usr/local/sbin/simp_rpm_helper +0 -495
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/CHANGELOG +0 -2
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/Rakefile +0 -3
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/build/rpm_metadata/requires +0 -2
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/data/os/CentOS.yaml +0 -2
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/data/os/RedHat.yaml +0 -2
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/hiera.yaml +0 -14
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/manifests/init.pp +0 -2
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-1.0/metadata.json +0 -37
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/CHANGELOG +0 -5
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/Rakefile +0 -3
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/build/rpm_metadata/requires +0 -2
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/data/os/CentOS.yaml +0 -2
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/data/os/RedHat.yaml +0 -2
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/hiera.yaml +0 -14
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/manifests/init.pp +0 -3
- data/spec/acceptance/files/package_upgrades/pupmod-simp-testpackage-2.0/metadata.json +0 -37
- data/spec/lib/simp/ci/files/job_broken_link_nodeset/spec/acceptance/suites/default/nodesets +0 -1
- data/spec/lib/simp/ci/files/job_invalid_nodeset/spec/acceptance/suites/default/nodesets +0 -1
- data/spec/lib/simp/ci/files/job_invalid_suite/spec/acceptance/suites/default/nodesets +0 -1
- data/spec/lib/simp/ci/files/job_missing_nodeset/spec/acceptance/suites/default/nodesets +0 -1
- data/spec/lib/simp/ci/files/job_missing_suite_and_nodeset/spec/acceptance/suites/default/nodesets +0 -1
- data/spec/lib/simp/ci/files/multiple_invalid_jobs/spec/acceptance/suites/default/nodesets +0 -1
- data/spec/lib/simp/ci/files/multiple_valid_jobs/spec/acceptance/suites/default/nodesets +0 -1
- data/spec/lib/simp/ci/files/no_gitlab_config_with_tests/spec/acceptance/suites/default/nodesets +0 -1
- data/spec/lib/simp/ci/files/no_gitlab_config_without_tests/spec/acceptance/suites/default/nodesets +0 -1
- data/spec/lib/simp/ci/files/suite_skeleton_only/spec/acceptance/nodesets/default.yml +0 -1
- data/spec/lib/simp/ci/files/suite_skeleton_only/spec/acceptance/suites/default/nodesets +0 -1
- data/spec/lib/simp/ci/files/valid_job_nodeset_dir_link/spec/acceptance/suites/default/nodesets +0 -1
- data/spec/lib/simp/ci/files/valid_job_nodeset_link/spec/acceptance/suites/default/nodesets/default.yml +0 -1
- data/spec/lib/simp/files/build/testpackage.spec +0 -1
- data/spec/lib/simp/rake/pupmod/fixtures/simpmod/spec/acceptance/nodesets/default.yml +0 -1
- data/spec/lib/simp/rake/pupmod/fixtures/simpmod/spec/acceptance/suites/default/nodesets +0 -1
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]
|
data/lib/simp/rake/rubygem.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
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
|
|
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
|
|
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,122 +497,6 @@ 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
|
|
615
|
-
end
|
|
616
|
-
|
|
617
500
|
# Returns the version of RPM installed on the system
|
|
618
501
|
def self.version
|
|
619
502
|
%x{rpm --version}.strip.split.last
|
|
@@ -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
|