diff --git a/features/commands/package.feature b/features/commands/package.feature index 76073cf48..8e4f0bea6 100644 --- a/features/commands/package.feature +++ b/features/commands/package.feature @@ -15,3 +15,20 @@ Feature: berks package """ Cookbook(s) packaged to """ + And the archive "my-cookbooks.tar.gz" should contain: + """ + cookbooks + cookbooks/fake + cookbooks/fake/attributes + cookbooks/fake/attributes/default.rb + cookbooks/fake/files + cookbooks/fake/files/default + cookbooks/fake/files/default/file.h + cookbooks/fake/metadata.json + cookbooks/fake/metadata.rb + cookbooks/fake/recipes + cookbooks/fake/recipes/default.rb + cookbooks/fake/templates + cookbooks/fake/templates/default + cookbooks/fake/templates/default/template.erb + """ diff --git a/features/step_definitions/utility_steps.rb b/features/step_definitions/utility_steps.rb index 87453ec83..29dd63d7f 100644 --- a/features/step_definitions/utility_steps.rb +++ b/features/step_definitions/utility_steps.rb @@ -2,6 +2,13 @@ skip end +Then("the archive {string} should contain:") do |path, expected_contents| + actual_contents = Zlib::GzipReader.open(expand_path(path)) do |gz| + Archive::Tar::Minitar::Input.each_entry(gz).map(&:full_name).join("\n") + end + expect(actual_contents).to eql(expected_contents) +end + Then /the output from \`(.+)\` should be the same as \`(.+)\`/ do |actual, expected| run(actual) actual_output = last_command_started.stdout diff --git a/lib/berkshelf/packager.rb b/lib/berkshelf/packager.rb index 92022b1ab..670845fa4 100644 --- a/lib/berkshelf/packager.rb +++ b/lib/berkshelf/packager.rb @@ -1,4 +1,5 @@ require "archive/tar/minitar" +require "find" unless defined?(Find) require "zlib" unless defined?(Zlib) module Berkshelf @@ -40,8 +41,18 @@ def initialize(out_file) # @return [String] # path to the generated archive def run(source) - tgz = Zlib::GzipWriter.new(File.open(out_file, "wb")) - Archive::Tar::Minitar.pack(Dir.glob("#{source}/*"), tgz) + begin + dest = Zlib::GzipWriter.open(out_file) + tar = RelativeTarWriter.new(dest, source) + Find.find(source) do |entry| + next if source == entry + + Archive::Tar::Minitar.pack_file(entry, tar) + end + ensure + tar.close + dest.close + end out_file rescue SystemCallError => ex @@ -67,5 +78,30 @@ def validate! # @return [String] attr_reader :filename + + # A private decorator for Archive::Tar::Minitar::Writer that + # turns absolute paths into relative ones. + class RelativeTarWriter < SimpleDelegator # :nodoc: + def initialize(io, base_path) + @base_path = Pathname.new(base_path) + super(Archive::Tar::Minitar::Writer.new(io)) + end + + %w{add_file add_file_simple mkdir}.each do |method| + class_eval <<~RUBY + def #{method}(name, *opts, &block) + super(relative_path(name), *opts, &block) + end + RUBY + end + + private + + attr_reader :base_path + + def relative_path(path) + Pathname.new(path).relative_path_from(base_path).to_s + end + end end end