vanagon 0.15.35 → 0.16.1

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.
@@ -115,6 +115,7 @@ class Vanagon
115
115
  # Docker engine specific
116
116
  attr_accessor :docker_image
117
117
  attr_accessor :docker_run_args
118
+ attr_accessor :use_docker_exec
118
119
 
119
120
  # AWS engine specific
120
121
  attr_accessor :aws_ami
@@ -238,6 +239,8 @@ class Vanagon
238
239
  @copy ||= "cp"
239
240
  @shasum ||= "sha1sum"
240
241
 
242
+ @use_docker_exec = false
243
+
241
244
  # Our first attempt at defining metadata about a platform
242
245
  @cross_compiled ||= false
243
246
  @valid_operators ||= ['<', '>', '<=', '>=', '=']
@@ -331,6 +334,10 @@ class Vanagon
331
334
  return !!@name.match(/^(el|redhat|redhatfips)-.*$/)
332
335
  end
333
336
 
337
+ def is_el8?
338
+ return !!@name.match(/^(el|redhat|redhatfips)-8.*$/)
339
+ end
340
+
334
341
  # Utility matcher to determine is the platform is a sles variety
335
342
  #
336
343
  # @return [true, false] true if it is a sles variety, false otherwise
@@ -482,9 +489,9 @@ class Vanagon
482
489
  def generate_compiled_archive(project)
483
490
  name_and_version = "#{project.name}-#{project.version}"
484
491
  name_and_version_and_platform = "#{name_and_version}.#{name}"
492
+ name_and_platform = "#{project.name}.#{name}"
485
493
  final_archive = "output/#{name_and_version_and_platform}.tar.gz"
486
494
  archive_directory = "#{project.name}-archive"
487
- metadata = project.build_manifest_json(true)
488
495
 
489
496
  # previously, we weren't properly handling the case of custom BOM paths.
490
497
  # If we have a custom BOM path, during Makefile execution, the top-level
@@ -499,7 +506,6 @@ class Vanagon
499
506
  bill_of_materials_command = "mv .#{project.bill_of_materials.path}/bill-of-materials ../.."
500
507
  end
501
508
 
502
- metadata.gsub!(/\n/, '\n')
503
509
  [
504
510
  "mkdir output",
505
511
  "mkdir #{archive_directory}",
@@ -507,7 +513,7 @@ class Vanagon
507
513
  "rm #{name_and_version}.tar.gz",
508
514
  "cd #{archive_directory}/#{name_and_version}; #{bill_of_materials_command}; #{tar} cf ../../#{name_and_version_and_platform}.tar *",
509
515
  "gzip -9c #{name_and_version_and_platform}.tar > #{name_and_version_and_platform}.tar.gz",
510
- "echo -e \"#{metadata}\" > output/#{name_and_version_and_platform}.json",
516
+ "cp build_metadata.#{name_and_platform}.json output/#{name_and_version_and_platform}.json",
511
517
  "cp bill-of-materials output/#{name_and_version_and_platform}-bill-of-materials ||:",
512
518
  "cp #{name_and_version_and_platform}.tar.gz output",
513
519
  "#{shasum} #{final_archive} > #{final_archive}.sha1"
@@ -140,7 +140,7 @@ class Vanagon
140
140
  @tar = "tar"
141
141
  @patch = "/usr/bin/patch"
142
142
  @num_cores = "/usr/bin/nproc"
143
- @curl = "curl --silent --show-error --fail"
143
+ @curl = "curl --silent --show-error --fail --location"
144
144
  @valid_operators = ['<', '>', '<=', '>=', '=', '<<', '>>']
145
145
  super(name)
146
146
  end
@@ -284,6 +284,17 @@ class Vanagon
284
284
  @platform.docker_run_args = Array(args)
285
285
  end
286
286
 
287
+ # Specify whether to use Docker exec instead of SSH to run commands
288
+ #
289
+ # This also causes Vanagon to use `docker cp` instead of `rsync` when
290
+ # copying files.
291
+ #
292
+ # @param bool [Boolean] a boolean value indicating whether to use
293
+ # `docker exec` and `docker cp` over `ssh` and `rsync`.
294
+ def use_docker_exec(bool)
295
+ @platform.use_docker_exec = bool
296
+ end
297
+
287
298
  # Set the ami for the platform to use
288
299
  #
289
300
  # @param ami [String] the ami id used.
@@ -103,7 +103,7 @@ class Vanagon
103
103
  @patch ||= "/usr/bin/patch"
104
104
  @num_cores ||= "/bin/grep -c 'processor' /proc/cpuinfo"
105
105
  @rpmbuild ||= "/usr/bin/rpmbuild"
106
- @curl = "curl --silent --show-error --fail"
106
+ @curl = "curl --silent --show-error --fail --location"
107
107
  super(name)
108
108
  end
109
109
  end
@@ -205,11 +205,36 @@ class Vanagon
205
205
  # "Misc Dir for versions.txt, License file and Icon file"
206
206
  misc_dir = "SourceDir/#{project.settings[:base_dir]}/#{project.settings[:company_id]}/#{project.settings[:product_id]}/misc"
207
207
  # Actual array of commands to be written to the Makefile
208
- [
208
+ make_commands = [
209
209
  "mkdir -p output/#{target_dir}",
210
210
  "mkdir -p $(tempdir)/{SourceDir,wix/wixobj}",
211
211
  "#{@copy} -r wix/* $(tempdir)/wix/",
212
- "gunzip -c #{project.name}-#{project.version}.tar.gz | '#{@tar}' -C '$(tempdir)/SourceDir' --strip-components 1 -xf -",
212
+ "gunzip -c #{project.name}-#{project.version}.tar.gz | '#{@tar}' -C '$(tempdir)/SourceDir' --strip-components 1 -xf -"
213
+ ]
214
+
215
+ unless project.extra_files_to_sign.empty?
216
+ begin
217
+ tempdir = nil
218
+ # Skip signing extra files if logging into the signing_host fails
219
+ # This enables things like CI being able to sign the additional files,
220
+ # but locally triggered builds by developers who don't have access to
221
+ # the signing host just print a message and skip the signing.
222
+ Vanagon::Utilities.retry_with_timeout(3, 5) do
223
+ tempdir = Vanagon::Utilities::remote_ssh_command("#{project.signing_username}@#{project.signing_hostname}", "#{@mktemp} 2>/dev/null", return_command_output: true)
224
+ end
225
+ project.extra_files_to_sign.each do |file|
226
+ file_location = File.join(tempdir, File.basename(file))
227
+ make_commands << [
228
+ "rsync -e '#{Vanagon::Utilities.ssh_command}' -rHlv --no-perms --no-owner --no-group #{File.join('$(tempdir)', 'SourceDir', file)} #{project.signing_username}@#{project.signing_hostname}:#{tempdir}",
229
+ "#{Vanagon::Utilities.ssh_command} #{project.signing_username}@#{project.signing_hostname} #{project.signing_command} #{file_location}",
230
+ "rsync -e '#{Vanagon::Utilities.ssh_command}' -rHlv -O --no-perms --no-owner --no-group #{project.signing_username}@#{project.signing_hostname}:#{file_location} #{File.join('$(tempdir)', 'SourceDir', file)}"
231
+ ]
232
+ end
233
+ rescue RuntimeError
234
+ warn "Unable to connect to #{project.signing_username}@#{project.signing_hostname}, skipping signing extra files: #{project.extra_files_to_sign.join(',')}"
235
+ end
236
+ end
237
+ make_commands << [
213
238
  "mkdir -p $(tempdir)/#{misc_dir}",
214
239
  # Need to use awk here to convert to DOS format so that notepad can display file correctly.
215
240
  "awk 'sub(\"$$\", \"\\r\")' $(tempdir)/SourceDir/bill-of-materials > $(tempdir)/#{misc_dir}/versions.txt",
@@ -225,6 +250,8 @@ class Vanagon
225
250
  # -loc is required for the UI localization it points to the actual localization .wxl
226
251
  "cd $(tempdir)/wix/wixobj; \"$$WIX/bin/light.exe\" #{light_flags} -b $$(cygpath -aw $(tempdir)) -loc $$(cygpath -aw $(tempdir)/wix/localization/puppet_en-us.wxl) -out $$(cygpath -aw $(workdir)/output/#{target_dir}/#{msi_package_name(project)}) *.wixobj",
227
252
  ]
253
+
254
+ make_commands.flatten
228
255
  end
229
256
 
230
257
  # Method to derive the msi (Windows Installer) package name for the project.
@@ -108,6 +108,14 @@ class Vanagon
108
108
  # you just want to perform installation and pull down a file.
109
109
  attr_accessor :no_packaging
110
110
 
111
+ # Extra files to sign
112
+ # Right now just supported on windows, useful for signing powershell scripts
113
+ # that need to be signed between build and MSI creation
114
+ attr_accessor :extra_files_to_sign
115
+ attr_accessor :signing_hostname
116
+ attr_accessor :signing_username
117
+ attr_accessor :signing_command
118
+
111
119
  # Loads a given project from the configdir
112
120
  #
113
121
  # @param name [String] the name of the project
@@ -156,6 +164,10 @@ class Vanagon
156
164
  @upstream_metadata = {}
157
165
  @no_packaging = false
158
166
  @artifacts_to_fetch = []
167
+ @extra_files_to_sign = []
168
+ @signing_hostname = ''
169
+ @signing_username = ''
170
+ @signing_command = ''
159
171
  end
160
172
 
161
173
  # Magic getter to retrieve settings in the project
@@ -714,20 +726,27 @@ class Vanagon
714
726
  # Writes a json file at `ext/build_metadata.<project>.<platform>.json` containing information
715
727
  # about what went into a built artifact
716
728
  #
717
- # @return [Hash] of build information
718
- def save_manifest_json(platform)
729
+ # @param platform [String] platform we're writing metadata for
730
+ # @param additional_directories [String|Array[String]] additional
731
+ # directories to write build_metadata.<project>.<platform>.json to
732
+ def save_manifest_json(platform, additional_directories = nil) # rubocop:disable Metrics/AbcSize
719
733
  manifest = build_manifest_json
720
734
  metadata = metadata_merge(manifest, @upstream_metadata)
721
735
 
722
736
  ext_directory = 'ext'
723
737
  FileUtils.mkdir_p ext_directory
724
738
 
739
+ directories = [ext_directory, additional_directories].compact
725
740
  metadata_file_name = "build_metadata.#{name}.#{platform.name}.json"
726
- File.open(File.join(ext_directory, metadata_file_name), 'w') do |f|
727
- f.write(JSON.pretty_generate(metadata))
741
+ directories.each do |directory|
742
+ File.open(File.join(directory, metadata_file_name), 'w') do |f|
743
+ f.write(JSON.pretty_generate(metadata))
744
+ end
728
745
  end
729
746
 
730
747
  ## VANAGON-132 Backwards compatibility: make a 'build_metadata.json' file
748
+ # No need to propagate this backwards compatibility to the new additional
749
+ # directories
731
750
  File.open(File.join(ext_directory, 'build_metadata.json'), 'w') do |f|
732
751
  f.write(JSON.pretty_generate(metadata))
733
752
  end
@@ -363,6 +363,39 @@ class Vanagon
363
363
  def no_packaging(var)
364
364
  @project.no_packaging = var
365
365
  end
366
+
367
+ # Set to sign additional files during buildtime. Only implemented for
368
+ # windows. Can be specified more than once
369
+ #
370
+ # @param [String] file to sign
371
+ def extra_file_to_sign(file)
372
+ @project.extra_files_to_sign << file
373
+ end
374
+
375
+ # The hostname to sign additional files on. Only does anything when there
376
+ # are extra files to sign
377
+ #
378
+ # @param [String] hostname of the machine to run the extra file signing on
379
+ def signing_hostname(hostname)
380
+ @project.signing_hostname = hostname
381
+ end
382
+
383
+ # The username to log in to the signing_hostname as. Only does anything
384
+ # when there are extra files to sign
385
+ #
386
+ # @param [String] the username to log in to `signing_hostname` as
387
+ def signing_username(username)
388
+ @project.signing_username = username
389
+ end
390
+
391
+ # The command to run to sign additional files. The command should assume
392
+ # it will have the file path appended to the end of the command, since
393
+ # files end up in a temp directory.
394
+ #
395
+ # @param [String] the command to sign additional files
396
+ def signing_command(command)
397
+ @project.signing_command = command
398
+ end
366
399
  end
367
400
  end
368
401
  end
@@ -33,6 +33,15 @@
33
33
  <%= var %>
34
34
  <% end -%>
35
35
 
36
+ # This breaks on el8. This is a hack to unblock development.
37
+ <%- if @platform.is_el8? %>
38
+ %undefine __debug_package
39
+
40
+ # Build el-8 packages without build-id files to prevent collision
41
+ %define _build_id_links none
42
+ <% end -%>
43
+
44
+
36
45
  # To avoid files installed but not packaged errors
37
46
  %global __os_install_post %{__os_install_post} \
38
47
  rm -rf %{buildroot}/usr/lib/debug
@@ -0,0 +1,80 @@
1
+ require 'vanagon/cli'
2
+
3
+ ##
4
+ ## Ignore the CLI calling 'exit'
5
+ ##
6
+ RSpec.configure do |rspec|
7
+ rspec.around(:example) do |ex|
8
+ begin
9
+ ex.run
10
+ rescue SystemExit => e
11
+ puts "Got SystemExit: #{e.inspect}. Ignoring"
12
+ end
13
+ end
14
+ end
15
+
16
+ describe Vanagon::CLI do
17
+ describe "options that don't take a value" do
18
+ [:skipcheck, :verbose].each do |flag|
19
+ it "can create an option parser that accepts the #{flag} flag" do
20
+ subject = described_class.new
21
+ expect(subject.parse(%W[build --#{flag} project platform])).to have_key(flag)
22
+ end
23
+ end
24
+
25
+ describe "short options" do
26
+ [["v", :verbose]].each do |short, long|
27
+ it "maps the short option #{short} to #{long}" do
28
+ subject = described_class.new
29
+ expect(subject.parse(%W[build -#{short} project platform])).to include(long => true)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "options that only allow limited values" do
36
+ [[:preserve, ["always", "never", "on-failure"]]].each do |option, values|
37
+ values.each do |value|
38
+ it "can create a parser that accepts \"--#{option} #{value}\"" do
39
+ subject = described_class.new
40
+ expect(subject.parse(%W[build --#{option} #{value} project platform]))
41
+ .to include(option => value.to_sym)
42
+ end
43
+ end
44
+ end
45
+ [[:preserve, ["bad-argument"]]].each do |option, values|
46
+ values.each do |value|
47
+ it "rejects the bad argument \"--#{option} #{value}\"" do
48
+ subject = described_class.new
49
+ expect{subject.parse(%W[build --#{option} #{value} project platform])}
50
+ .to raise_error(Vanagon::InvalidArgument)
51
+ end
52
+ end
53
+ end
54
+ it "preserve defaults to :on-failure" do
55
+ subject = described_class.new
56
+ expect(subject.parse([])).to include(:preserve => :'on-failure')
57
+ end
58
+ end
59
+
60
+
61
+ describe "options that take a value" do
62
+ [:workdir, :configdir, :engine].each do |option|
63
+ it "can create an option parser that accepts the #{option} option" do
64
+ subject = described_class.new
65
+ expect(subject.parse(%W[build --#{option} hello project platform]))
66
+ .to include(option => "hello")
67
+ end
68
+ end
69
+
70
+ describe "short options" do
71
+ [["w", :workdir], ["c", :configdir], ["e", :engine]].each do |short, long|
72
+ it "maps the short option #{short} to #{long}" do
73
+ subject = described_class.new
74
+ expect(subject.parse(%W[build -#{short} hello project platform]))
75
+ .to include(long => "hello")
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,21 +1,36 @@
1
1
  require 'vanagon/engine/docker'
2
2
  require 'vanagon/platform'
3
3
 
4
- describe 'Vanagon::Engine::Docker' do
5
- let (:platform_with_docker_image) {
6
- plat = Vanagon::Platform::DSL.new('debian-6-i386')
7
- plat.instance_eval("platform 'debian-6-i386' do |plat|
8
- plat.docker_image 'debian-6-i386'
9
- end")
4
+ describe Vanagon::Engine::Docker do
5
+ let (:platform_with_docker_image) do
6
+ plat = Vanagon::Platform::DSL.new('debian-10-amd64')
7
+ plat.instance_eval(<<~EOF)
8
+ platform 'debian-10-amd64' do |plat|
9
+ plat.docker_image 'debian:10-slim'
10
+ end
11
+ EOF
10
12
  plat._platform
11
- }
13
+ end
12
14
 
13
- let (:platform_without_docker_image) {
14
- plat = Vanagon::Platform::DSL.new('debian-6-i386')
15
- plat.instance_eval("platform 'debian-6-i386' do |plat|
16
- end")
15
+ let (:platform_without_docker_image) do
16
+ plat = Vanagon::Platform::DSL.new('debian-10-amd64')
17
+ plat.instance_eval(<<~EOF)
18
+ platform 'debian-10-amd64' do |plat|
19
+ end
20
+ EOF
21
+ plat._platform
22
+ end
23
+
24
+ let(:platform_with_docker_exec) do
25
+ plat = Vanagon::Platform::DSL.new('debian-10-amd64')
26
+ plat.instance_eval(<<~EOF)
27
+ platform 'debian-10-amd64' do |plat|
28
+ plat.docker_image 'debian:10-slim'
29
+ plat.use_docker_exec true
30
+ end
31
+ EOF
17
32
  plat._platform
18
- }
33
+ end
19
34
 
20
35
  describe '#initialize' do
21
36
  it 'fails without docker installed' do
@@ -23,24 +38,67 @@ describe 'Vanagon::Engine::Docker' do
23
38
  expect(FileTest).to receive(:executable?).with(File.join(path_elem, 'docker')).and_return(false)
24
39
  end
25
40
 
26
- expect { Vanagon::Engine::Docker.new(platform_with_docker_image) }.to raise_error(RuntimeError)
41
+ expect { described_class.new(platform_with_docker_image) }.to raise_error(RuntimeError)
27
42
  end
28
43
  end
29
44
 
30
45
  describe "#validate_platform" do
31
46
  it 'raises an error if the platform is missing a required attribute' do
32
47
  expect(Vanagon::Utilities).to receive(:find_program_on_path).with('docker').and_return('/usr/bin/docker')
33
- expect { Vanagon::Engine::Docker.new(platform_without_docker_image).validate_platform }.to raise_error(Vanagon::Error)
48
+ expect { described_class.new(platform_without_docker_image).validate_platform }.to raise_error(Vanagon::Error)
34
49
  end
35
50
 
36
51
  it 'returns true if the platform has the required attributes' do
37
52
  expect(Vanagon::Utilities).to receive(:find_program_on_path).with('docker').and_return('/usr/bin/docker')
38
- expect(Vanagon::Engine::Docker.new(platform_with_docker_image).validate_platform).to be(true)
53
+ expect(described_class.new(platform_with_docker_image).validate_platform).to be(true)
39
54
  end
40
55
  end
41
56
 
42
57
  it 'returns "docker" name' do
43
58
  expect(Vanagon::Utilities).to receive(:find_program_on_path).with('docker').and_return('/usr/bin/docker')
44
- expect(Vanagon::Engine::Docker.new(platform_with_docker_image).name).to eq('docker')
59
+ expect(described_class.new(platform_with_docker_image).name).to eq('docker')
60
+ end
61
+
62
+ describe '#dispatch' do
63
+ context 'when platform has use_docker_exec set' do
64
+ subject { described_class.new(platform_with_docker_exec) }
65
+
66
+ it 'uses docker exec' do
67
+ expect(Vanagon::Utilities).to receive(:remote_ssh_command).never
68
+ expect(subject).to receive(:docker_exec)
69
+
70
+ subject.dispatch('true', true)
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#ship_workdir' do
76
+ context 'when platform has use_docker_exec set' do
77
+ subject { described_class.new(platform_with_docker_exec) }
78
+
79
+ it 'uses docker cp' do
80
+ expect(Vanagon::Utilities).to receive(:rsync_to).never
81
+ expect(subject).to receive(:docker_cp_globs_to)
82
+
83
+ subject.ship_workdir('foo/')
84
+ end
85
+ end
86
+ end
87
+
88
+ describe '#retrieve_built_artifact' do
89
+ context 'when platform has use_docker_exec set' do
90
+ subject { described_class.new(platform_with_docker_exec) }
91
+
92
+ before(:each) do
93
+ allow(FileUtils).to receive(:mkdir_p)
94
+ end
95
+
96
+ it 'uses docker cp' do
97
+ expect(Vanagon::Utilities).to receive(:rsync_from).never
98
+ expect(subject).to receive(:docker_cp_globs_from)
99
+
100
+ subject.retrieve_built_artifact('output/*', false)
101
+ end
102
+ end
45
103
  end
46
104
  end