diff --git a/lib/inspec/cli.rb b/lib/inspec/cli.rb index 8a7a155d7..6bc1da21a 100644 --- a/lib/inspec/cli.rb +++ b/lib/inspec/cli.rb @@ -75,6 +75,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength desc 'archive PATH', 'archive a profile to tar.gz (default) or zip' profile_options + option :output, aliases: :o, type: :string, + desc: 'Save the archive to a path' option :zip, type: :boolean, default: false, desc: 'Generates a zip archive.' option :tar, type: :boolean, default: false, diff --git a/lib/inspec/profile.rb b/lib/inspec/profile.rb index 6b2c22cb3..bb456f902 100644 --- a/lib/inspec/profile.rb +++ b/lib/inspec/profile.rb @@ -173,26 +173,17 @@ module Inspec # generates a archive of a folder profile # assumes that the profile was checked before - def archive(opts) # rubocop:disable Metrics/AbcSize - profile_name = params[:name] - ext = opts[:zip] ? 'zip' : 'tar.gz' - - if opts[:archive] - archive = Pathname.new(opts[:archive]) - else - slug = profile_name.downcase.strip.tr(' ', '-').gsub(/[^\w-]/, '_') - archive = Pathname.new(Dir.pwd).join("#{slug}.#{ext}") - end - + def archive(opts) # check if file exists otherwise overwrite the archive - if archive.exist? && !opts[:overwrite] - @logger.info "Archive #{archive} exists already. Use --overwrite." + dst = archive_name(opts) + if dst.exist? && !opts[:overwrite] + @logger.info "Archive #{dst} exists already. Use --overwrite." return false end # remove existing archive - File.delete(archive) if archive.exist? - @logger.info "Generate archive #{archive}." + File.delete(dst) if dst.exist? + @logger.info "Generate archive #{dst}." # filter files that should not be part of the profile # TODO ignore all .files, but add the files to debug output @@ -207,12 +198,12 @@ module Inspec # generate zip archive require 'inspec/archive/zip' zag = Inspec::Archive::ZipArchiveGenerator.new - zag.archive(root_path, files, archive) + zag.archive(root_path, files, dst) else # generate tar archive require 'inspec/archive/tar' tag = Inspec::Archive::TarArchiveGenerator.new - tag.archive(root_path, files, archive) + tag.archive(root_path, files, dst) end @logger.info 'Finished archive generation.' @@ -221,6 +212,24 @@ module Inspec private + # Create an archive name for this profile and an additional options + # configuration. Either use :output or generate the name from metadata. + # + # @param [Hash] configuration options + # @return [Pathname] path for the archive + def archive_name(opts) + if (name = opts[:output]) + return Pathname.new(name) + end + + name = params[:name] || + fail('Cannot create an archive without a profile name! Please '\ + 'specify the name in metadata or use --output to create the archive.') + ext = opts[:zip] ? 'zip' : 'tar.gz' + slug = name.downcase.strip.tr(' ', '-').gsub(/[^\w-]/, '_') + Pathname.new(Dir.pwd).join("#{slug}.#{ext}") + end + def load_params params = @source_reader.metadata.params params[:name] = @profile_id unless @profile_id.nil? diff --git a/test/functional/command_test.rb b/test/functional/command_test.rb index 686a1fc24..c74184e69 100644 --- a/test/functional/command_test.rb +++ b/test/functional/command_test.rb @@ -18,6 +18,13 @@ describe 'Inspec::InspecCLI' do let(:exec_inspec) { File.join(repo_path, 'bin', 'inspec') } let(:profile_path) { File.join(repo_path, 'test', 'unit', 'mock', 'profiles') } let(:examples_path) { File.join(repo_path, 'examples') } + let(:dst) { + # create a temporary path, but we only want an auto-clean helper + # so remove the file and give back the path + res = Tempfile.new('inspec-shred') + FileUtils.rm(res.path) + res + } def inspec(commandline) CMD.run_command("#{exec_inspec} #{commandline}") @@ -38,6 +45,54 @@ describe 'Inspec::InspecCLI' do out.stdout.must_match /Generate archive [^ ]*profile.tar.gz/ out.stdout.must_include 'Finished archive generation.' end + + it 'archives to output file' do + out = inspec('archive ' + path + ' --output ' + dst.path) + out.stderr.must_equal '' + out.stdout.must_include 'Generate archive '+dst.path + out.stdout.must_include 'Finished archive generation.' + out.exit_status.must_equal 0 + File.exist?(dst.path).must_equal true + end + + it 'auto-archives when no --output is given' do + auto_dst = File.join(repo_path, 'profile.tar.gz') + out = inspec('archive ' + path + ' --overwrite') + out.stderr.must_equal '' + out.stdout.must_include 'Generate archive '+auto_dst + out.stdout.must_include 'Finished archive generation.' + out.exit_status.must_equal 0 + File.exist?(auto_dst).must_equal true + end + + it 'archive on invalid archive' do + out = inspec('archive /proc --output ' + dst.path) + # out.stdout.must_equal '' => we have partial stdout output right now + out.stderr.must_include "Don't understand inspec profile in \"/proc\"" + out.exit_status.must_equal 1 + File.exist?(dst.path).must_equal false + end + + it 'archive wont overwrite existing files' do + x = rand.to_s + File.write(dst.path, x) + out = inspec('archive ' + path + ' --output ' + dst.path) + out.stderr.must_equal '' # uh... + out.stdout.must_include "Archive #{dst.path} exists already. Use --overwrite." + out.exit_status.must_equal 1 + File.read(dst.path).must_equal x + end + + it 'archive will overwrite files if necessary' do + x = rand.to_s + File.write(dst.path, x) + out = inspec('archive ' + path + ' --output ' + dst.path + ' --overwrite') + out.stderr.must_equal '' # uh... + out.stdout.must_include 'Generate archive '+dst.path + out.exit_status.must_equal 0 + File.read(dst.path).wont_equal x + end + end describe 'example inheritance profile' do @@ -46,26 +101,32 @@ describe 'Inspec::InspecCLI' do it 'check fails without --profiles-path' do out = inspec('check ' + path) out.stderr.must_include 'You must supply a --profiles-path to inherit' + out.stdout.must_equal '' out.exit_status.must_equal 1 end it 'check succeeds with --profiles-path' do out = inspec('check ' + path + ' --profiles-path ' + examples_path) + out.stderr.must_equal '' out.stdout.must_match /Valid.*true/ out.exit_status.must_equal 0 end it 'archive fails without --profiles-path' do - out = inspec('archive ' + path + ' --overwrite') + out = inspec('archive ' + path + ' --output ' + dst.path) + # out.stdout.must_equal '' => we have partial stdout output right now out.stderr.must_include 'You must supply a --profiles-path to inherit' out.exit_status.must_equal 1 + File.exist?(dst.path).must_equal false end it 'archive is successful with --profiles-path' do - out = inspec('archive ' + path + ' --overwrite --profiles-path ' + examples_path) - out.exit_status.must_equal 0 - out.stdout.must_match /Generate archive [^ ]*inheritance.tar.gz/ + out = inspec('archive ' + path + ' --output ' + dst.path + ' --profiles-path ' + examples_path) + out.stderr.must_equal '' + out.stdout.must_include 'Generate archive '+dst.path out.stdout.must_include 'Finished archive generation.' + out.exit_status.must_equal 0 + File.exist?(dst.path).must_equal true end end end