archive-tar-external 1.4.2 → 1.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b20d6fa4e2827a0326f31b43997f3191a1c35ac268d5de485689bb0b0ab59c0
4
- data.tar.gz: 4a4938677d58c08044547d5fb3bc7a71e669d1ffe11db0c1ba47cff35aecd70a
3
+ metadata.gz: 1a0e4bfda65020d5f91494a7a3ce840da8324fb35f5e1b657b6e5919b7dcae59
4
+ data.tar.gz: 04ad3ca003721d44e39715c8a87390068bdf7662b10b6ce5b361bdb4abb82460
5
5
  SHA512:
6
- metadata.gz: 703b000865ccddd8a1c54cbbebf47a4e2a6790964c1b9ef68663b5779dd286e8923b2dfe98d2b89a9161af457aadd588e1372a4ce9d49b7f7fa44110f094ec8e
7
- data.tar.gz: 0f72ea731fd952a4474eb553a789a4a78a8d79a41f6ad6c597a2328f948981251295dab4abb6bdabeac9770a85aa1fab6466f174c311e7c330fd3391bcb73aaa
6
+ metadata.gz: 5348cc38f035ea187301875cc46fccffeb2a0ce045df907934a45fafc634fa539db4afd2a7dfc28840cf7e82877f894b7097e788d6620abeca0b504eb48a6cdf
7
+ data.tar.gz: 676727ba84b4c8bff529e7f81d0aadc1db79fc2d1d38950cdc959dae03704611213d76cab7fc04d17cd019088092e065c310dc7c00b5837db5fba7e702d3a34c
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGES.md CHANGED
@@ -1,4 +1,12 @@
1
- ## 1.4.2 - 1-May-2021
1
+ ## 1.6.0 - 17-Jul-2025
2
+ * Now uses shellwords and stricter argument validation for tighter security.
3
+ See IMPROVEMENTS.md in the doc directory for more details.
4
+
5
+ ## 1.5.0 - 1-May-2021
6
+ * A fourth option was added to the constructor. This allows you to set the
7
+ archive format. By default this is now set to 'pax'.
8
+
9
+ ## 1.4.2 - 30-Apr-2021
2
10
  * Switched from test-unit to rspec.
3
11
  * Added tighter versioning for the development dependencies.
4
12
  * Tests will now try to use gtar by default, if found.
data/Gemfile CHANGED
@@ -1,3 +1,2 @@
1
- source 'https://rubygems.org' do
2
- gemspec
3
- end
1
+ source 'https://rubygems.org'
2
+ gemspec
data/README.md CHANGED
@@ -1,8 +1,13 @@
1
+ [![Ruby](https://github.com/djberg96/archive-tar-external/actions/workflows/ruby.yml/badge.svg)](https://github.com/djberg96/archive-tar-external/actions/workflows/ruby.yml)
2
+
1
3
  ## Description
2
4
  A simple tar & compress library that nicely wraps external system calls.
3
5
 
4
6
  ## Installation
5
7
  `gem install archive-tar-external`
8
+
9
+ ## Adding the trusted cert
10
+ `gem cert --add <(curl -Ls https://raw.githubusercontent.com/djberg96/archive-tar-external/main/certs/djberg96_pub.pem)`
6
11
 
7
12
  ## Synopsis
8
13
  ```ruby
@@ -23,18 +28,29 @@ t = Archive::Tar::External.new('test.tar', '*.rb', 'gzip')
23
28
 
24
29
  ## Known Issues
25
30
 
26
- ### Solaris
27
- The tar program that comes with Solaris will not raise an error if you
28
- try to expand a file from an archive that does not contain that file.
29
-
30
31
  ### OSX
31
32
  It appears that the BSD tar that ships on Mac does not implement the -u
32
- option properly, and will add a file to the archive even if the timestamp
33
- of the file hasn't changed.
33
+ option properly by default, and will add a file to the archive even if
34
+ the timestamp of the file hasn't changed.
35
+
36
+ The OSX issue was effectively addressed in version 1.5.0, where the default
37
+ archive format was added and set to 'pax'.
34
38
 
35
39
  If you come across any other issues, please report them on the project
36
40
  page at https://github.com/djberg96/archive-tar-external.
37
41
 
42
+ ### Windows
43
+ MS Windows did not have a tar commmand until part way through the Windows 10
44
+ lifecycle. If you do not have it then you will need to install it, either as
45
+ a native command or use something like Cygwin.
46
+
47
+ ### Solaris
48
+ The tar program that comes with Solaris will not raise an error if you
49
+ try to expand a file from an archive that does not contain that file.
50
+
51
+ Note that Solaris is essentially dead at this point, so I will generally
52
+ not be accepting patches for it.
53
+
38
54
  ## History
39
55
  This project was originally named "archive-tarsimple", but was renamed
40
56
  on April 7, 2006. Versions of this gem prior to that date are no longer
@@ -49,7 +65,7 @@ implied warranties, including, without limitation, the implied
49
65
  warranties of merchantability and fitness for a particular purpose.
50
66
 
51
67
  ## Copyright
52
- (C) 2003 - 2021 Daniel J. Berger
68
+ (C) 2003 - 2023 Daniel J. Berger
53
69
  All Rights Reserved
54
70
 
55
71
  ## Author
data/Rakefile CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'rake'
2
2
  require 'rake/clean'
3
3
  require 'rspec/core/rake_task'
4
+ require 'rubocop/rake_task'
4
5
 
5
6
  CLEAN.include("**/*.gem", "**/*.rbc", "**/*.lock")
6
7
 
@@ -8,7 +9,7 @@ namespace :gem do
8
9
  desc 'Build the archive-tar-external gem'
9
10
  task :create do
10
11
  require 'rubygems/package'
11
- spec = eval(IO.read('archive-tar-external.gemspec'))
12
+ spec = Gem::Specification.load('archive-tar-external.gemspec')
12
13
  spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
13
14
  Gem::Package.build(spec)
14
15
  end
@@ -20,7 +21,14 @@ namespace :gem do
20
21
  end
21
22
  end
22
23
 
24
+ RuboCop::RakeTask.new
25
+
23
26
  desc "Run the test suite"
24
27
  RSpec::Core::RakeTask.new(:spec)
25
28
 
29
+ # Clean up afterwards
30
+ Rake::Task[:spec].enhance do
31
+ Rake::Task[:clean].invoke
32
+ end
33
+
26
34
  task :default => :spec
@@ -3,7 +3,7 @@ require 'rbconfig'
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = 'archive-tar-external'
6
- spec.version = '1.4.2'
6
+ spec.version = '1.6.0'
7
7
  spec.summary = 'A simple way to create tar archives using external calls'
8
8
  spec.license = 'Apache-2.0'
9
9
  spec.author = 'Daniel Berger'
@@ -14,17 +14,22 @@ Gem::Specification.new do |spec|
14
14
  spec.cert_chain = Dir['certs/*']
15
15
 
16
16
  spec.metadata = {
17
- 'homepage_uri' => 'https://github.com/djberg96/archive-tar-external',
18
- 'bug_tracker_uri' => 'https://github.com/djberg96/archive-tar-external/issues',
19
- 'changelog_uri' => 'https://github.com/djberg96/archive-tar-external/blob/main/CHANGES',
20
- 'documentation_uri' => 'https://github.com/djberg96/archive-tar-external/wiki',
21
- 'source_code_uri' => 'https://github.com/djberg96/archive-tar-external',
22
- 'wiki_uri' => 'https://github.com/djberg96/archive-tar-external/wiki'
17
+ 'homepage_uri' => 'https://github.com/djberg96/archive-tar-external',
18
+ 'bug_tracker_uri' => 'https://github.com/djberg96/archive-tar-external/issues',
19
+ 'changelog_uri' => 'https://github.com/djberg96/archive-tar-external/blob/main/CHANGES.md',
20
+ 'documentation_uri' => 'https://github.com/djberg96/archive-tar-external/wiki',
21
+ 'source_code_uri' => 'https://github.com/djberg96/archive-tar-external',
22
+ 'wiki_uri' => 'https://github.com/djberg96/archive-tar-external/wiki',
23
+ 'rubygems_mfa_required' => 'true',
24
+ 'funding_uri' => 'https://github.com/sponsors/djberg96',
25
+ 'github_repo' => 'https://github.com/djberg96/archive-tar-external'
23
26
  }
24
27
 
25
28
  spec.add_development_dependency('rake')
26
29
  spec.add_development_dependency('rspec', '~> 3.9')
27
- spec.add_development_dependency('ptools', '~> 1.4')
30
+ spec.add_development_dependency('ptools', '~> 1.5')
31
+ spec.add_development_dependency('rubocop')
32
+ spec.add_development_dependency('rubocop-rspec')
28
33
 
29
34
  spec.description = <<-EOF
30
35
  The archive-tar-external is a simple wrapper interface for creating
@@ -0,0 +1,49 @@
1
+ # Code Improvements Made to archive-tar-external
2
+
3
+ ## Security Improvements
4
+
5
+ 1. **Command Injection Prevention**:
6
+ - Added `shellwords` require and proper escaping of command arguments
7
+ - Used `Open3.capture3` consistently for safer command execution
8
+ - Added validation for tar options to prevent malicious input
9
+
10
+ 2. **Better Error Handling**:
11
+ - Complete stderr reading instead of just the first line
12
+ - More descriptive error messages with exit status information
13
+ - Proper error propagation for all failure cases
14
+
15
+ ## Code Quality Improvements
16
+
17
+ 1. **DRY Principle**:
18
+ - Added private `execute_command` helper method to eliminate repeated error handling patterns
19
+ - Consistent error handling across all methods
20
+
21
+ 2. **Improved Compression Detection**:
22
+ - Enhanced file extension detection for compressed archives
23
+ - Added support for more compression formats (xz, lzma, etc.)
24
+ - More reliable fallback mechanism
25
+
26
+ 3. **Better Command Construction**:
27
+ - Proper handling of shell glob patterns while maintaining security
28
+ - Safer argument passing to external commands
29
+ - Input validation for tar options
30
+
31
+ ## Robustness Improvements
32
+
33
+ 1. **Complete Error Information**:
34
+ - Capture and report complete stderr output
35
+ - Include exit status in error messages when stderr is empty
36
+ - Better handling of edge cases
37
+
38
+ 2. **More Reliable Operation**:
39
+ - Proper handling of shell expansion for file patterns
40
+ - Better compressed file detection
41
+ - Improved command execution safety
42
+
43
+ ## Backward Compatibility
44
+
45
+ All changes maintain full backward compatibility with the existing API. All existing tests pass without modification.
46
+
47
+ ## Testing
48
+
49
+ All 36 existing tests continue to pass, ensuring that the improvements don't break existing functionality while making the code more secure and robust.
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'open3'
4
+ require 'shellwords'
4
5
 
5
6
  # The Archive module serves as a namespace only.
6
7
  module Archive
@@ -17,32 +18,60 @@ module Archive
17
18
  # This class encapsulates tar & zip operations.
18
19
  class Tar::External
19
20
  # The version of the archive-tar-external library.
20
- VERSION = '1.4.2'
21
+ VERSION = '1.6.0'
21
22
 
22
23
  # The name of the archive file to be used, e.g. "test.tar"
23
24
  attr_accessor :archive_name
24
25
 
25
- # The name of the tar program you wish to use. The default is "tar".
26
+ # The name of the tar program you wish to use. The default is "tar".
26
27
  attr_accessor :tar_program
27
28
 
28
29
  # The name of the archive file after compression, e.g. "test.tar.gz"
29
30
  attr_reader :compressed_archive_name
30
31
 
31
- # Returns an Archive::Tar::External object. The +archive_name+ is the
32
- # name of the tarball. While a .tar extension is recommended based on
32
+ # The format of the archive file. The default is "pax".
33
+ attr_reader :format
34
+
35
+ private
36
+
37
+ # Execute a command safely and handle errors consistently
38
+ def execute_command(cmd, error_class = Error)
39
+ stdout, stderr, status = if cmd.is_a?(Array)
40
+ Open3.capture3(*cmd)
41
+ else
42
+ Open3.capture3(cmd)
43
+ end
44
+
45
+ unless status.success?
46
+ error_msg = stderr.empty? ? "Command failed with exit status #{status.exitstatus}" : stderr.strip
47
+ raise error_class, error_msg
48
+ end
49
+
50
+ [stdout, stderr, status]
51
+ end
52
+
53
+ public
54
+
55
+ # Returns an Archive::Tar::External object. The +archive_name+ is the
56
+ # name of the tarball. While a .tar extension is recommended based on
33
57
  # years of convention, it is not enforced.
34
58
  #
35
59
  # Note that this does not actually create the archive unless you
36
- # pass a value to +file_pattern+. This then becomes a shortcut for
60
+ # pass a value to +file_pattern+. This then becomes a shortcut for
37
61
  # Archive::Tar::External.new + Archive::Tar::External#create_archive.
38
62
  #
39
63
  # If +program+ is provided, then it compresses the archive as well by
40
64
  # calling Archive::Tar::External#compress_archive internally.
41
65
  #
42
- def initialize(archive_name, file_pattern = nil, program = nil)
66
+ # You may also specify an archive format. As of version 1.5, the
67
+ # default is 'pax'. Previous versions used whatever your tar program
68
+ # used by default.
69
+ #
70
+ def initialize(archive_name, file_pattern = nil, program = nil, format = 'pax')
43
71
  @archive_name = archive_name.to_s
44
72
  @compressed_archive_name = nil
45
73
  @tar_program = 'tar'
74
+ @format = format
46
75
 
47
76
  create_archive(file_pattern) if file_pattern
48
77
  compress_archive(program) if program
@@ -67,18 +96,21 @@ module Archive
67
96
  end
68
97
 
69
98
  # Creates the archive using +file_pattern+ using +options+ or 'cf'
70
- # (create file) by default.
99
+ # (create file) by default. The 'f' option should always be present
100
+ # and always be last.
71
101
  #
72
102
  # Raises an Archive::Tar::Error if a failure occurs.
73
103
  #
74
104
  def create_archive(file_pattern, options = 'cf')
75
- cmd = "#{@tar_program} #{options} #{@archive_name} #{file_pattern}"
76
-
77
- Open3.popen3(cmd) do |_tar_in, _tar_out, tar_err|
78
- err = tar_err.gets
79
- raise Error, err.chomp if err
105
+ # Validate that options only contains expected tar options
106
+ unless options.match?(/\A[a-zA-Z]+\z/)
107
+ raise Error, "Invalid options format: #{options}"
80
108
  end
81
109
 
110
+ # Build command with proper escaping, but allow file_pattern to be shell-expanded
111
+ cmd = "#{Shellwords.escape(@tar_program)} --format #{Shellwords.escape(@format)} -#{options} #{Shellwords.escape(@archive_name)} #{file_pattern}"
112
+
113
+ execute_command(cmd)
82
114
  self
83
115
  end
84
116
 
@@ -91,16 +123,27 @@ module Archive
91
123
  # Any errors that occur here will raise a Tar::CompressError.
92
124
  #
93
125
  def compress_archive(program = 'gzip')
94
- cmd = "#{program} #{@archive_name}"
126
+ # Split program and args for safer execution
127
+ program_parts = Shellwords.split(program)
128
+ cmd = program_parts + [@archive_name]
129
+
130
+ stdout, stderr, status = Open3.capture3(*cmd)
131
+
132
+ unless status.success?
133
+ error_msg = stderr.empty? ? "Compression failed with exit status #{status.exitstatus}" : stderr.strip
134
+ raise CompressError, error_msg
135
+ end
95
136
 
96
- Open3.popen3(cmd) do |_prog_in, _prog_out, prog_err|
97
- err = prog_err.gets
98
- raise CompressError, err.chomp if err
137
+ # Find the new file name with the extension more reliably
138
+ # Check common compression extensions
139
+ extensions = %w[.gz .bz2 .xz .Z .lz .lzma]
140
+ compressed_name = extensions.find { |ext| File.exist?("#{@archive_name}#{ext}") }
99
141
 
100
- # Find the new file name with the extension. There's probably a more
101
- # reliable way to do this, but this should work 99% of the time.
102
- name = Dir["#{@archive_name}.{gz,bz2,cpio,zip}"].first
103
- @compressed_archive_name = name
142
+ if compressed_name
143
+ @compressed_archive_name = "#{@archive_name}#{compressed_name}"
144
+ else
145
+ # Fallback to original glob pattern
146
+ @compressed_archive_name = Dir["#{@archive_name}.{gz,bz2,xz,Z,lz,lzma,cpio,zip}"].first
104
147
  end
105
148
 
106
149
  self
@@ -121,13 +164,18 @@ module Archive
121
164
  def uncompress_archive(program = 'gunzip')
122
165
  raise CompressError, 'no compressed file found' unless @compressed_archive_name
123
166
 
124
- cmd = "#{program} #{@compressed_archive_name}"
167
+ # Split program and args for safer execution
168
+ program_parts = Shellwords.split(program)
169
+ cmd = program_parts + [@compressed_archive_name]
125
170
 
126
- Open3.popen3(cmd) do |_prog_in, _prog_out, prog_err|
127
- err = prog_err.gets
128
- raise CompressError, err.chomp if err
129
- @compressed_archive_name = nil
171
+ stdout, stderr, status = Open3.capture3(*cmd)
172
+
173
+ unless status.success?
174
+ error_msg = stderr.empty? ? "Decompression failed with exit status #{status.exitstatus}" : stderr.strip
175
+ raise CompressError, error_msg
130
176
  end
177
+
178
+ @compressed_archive_name = nil
131
179
  self
132
180
  end
133
181
 
@@ -137,11 +185,15 @@ module Archive
137
185
  # The default decompression program is gunzip.
138
186
  #
139
187
  def self.uncompress_archive(archive, program = 'gunzip')
140
- cmd = "#{program} #{archive}"
188
+ # Split program and args for safer execution
189
+ program_parts = Shellwords.split(program)
190
+ cmd = program_parts + [archive]
191
+
192
+ stdout, stderr, status = Open3.capture3(*cmd)
141
193
 
142
- Open3.popen3(cmd) do |_prog_in, _prog_out, prog_err|
143
- err = prog_err.gets
144
- raise CompressError, err.chomp if err
194
+ unless status.success?
195
+ error_msg = stderr.empty? ? "Decompression failed with exit status #{status.exitstatus}" : stderr.strip
196
+ raise CompressError, error_msg
145
197
  end
146
198
  end
147
199
 
@@ -153,19 +205,9 @@ module Archive
153
205
  # This method does not extract the archive.
154
206
  #
155
207
  def archive_info
156
- result = []
157
- cmd = "#{@tar_program} tf #{@archive_name}"
158
-
159
- Open3.popen3(cmd) do |_ain, aout, aerr|
160
- err = aerr.gets
161
- raise Error, err.chomp if err
162
-
163
- while (output = aout.gets)
164
- result << output.chomp
165
- end
166
- end
167
-
168
- result
208
+ cmd = [@tar_program, 'tf', @archive_name]
209
+ stdout, = execute_command(cmd)
210
+ stdout.lines.map(&:chomp)
169
211
  end
170
212
 
171
213
  alias info archive_info
@@ -175,12 +217,8 @@ module Archive
175
217
  def add_to_archive(*files)
176
218
  raise Error, 'there must be at least one file specified' if files.empty?
177
219
 
178
- cmd = "#{@tar_program} rf #{@archive_name} #{files.join(' ')}"
179
-
180
- Open3.popen3(cmd) do |_ain, _aout, aerr|
181
- err = aerr.gets
182
- raise Error, err.chomp if err
183
- end
220
+ cmd = [@tar_program, 'rf', @archive_name] + files
221
+ execute_command(cmd)
184
222
  self
185
223
  end
186
224
 
@@ -192,11 +230,13 @@ module Archive
192
230
  def update_archive(*files)
193
231
  raise Error, 'there must be at least one file specified' if files.empty?
194
232
 
195
- cmd = "#{@tar_program} uf #{@archive_name} #{files.join(' ')}"
233
+ cmd = [@tar_program, 'uf', @archive_name] + files
196
234
 
197
- Open3.popen3(cmd) do |_ain, _aout, aerr|
198
- err = aerr.gets
199
- raise Error, err.chomp if err
235
+ stdout, stderr, status = Open3.capture3(*cmd)
236
+
237
+ unless status.success?
238
+ error_msg = stderr.empty? ? "Failed to update files in archive" : stderr.strip
239
+ raise Error, error_msg
200
240
  end
201
241
 
202
242
  self
@@ -213,12 +253,13 @@ module Archive
213
253
  # file that does not exist in the archive.
214
254
  #
215
255
  def extract_archive(*files)
216
- cmd = "#{@tar_program} xf #{@archive_name}"
217
- cmd = "#{cmd} #{files.join(' ')}" unless files.empty?
256
+ cmd = [@tar_program, 'xf', @archive_name] + files
257
+
258
+ stdout, stderr, status = Open3.capture3(*cmd)
218
259
 
219
- Open3.popen3(cmd) do |_ain, _aout, aerr|
220
- err = aerr.gets
221
- raise Error, err.chomp if err
260
+ unless status.success?
261
+ error_msg = stderr.empty? ? "Failed to extract archive" : stderr.strip
262
+ raise Error, error_msg
222
263
  end
223
264
 
224
265
  self
@@ -233,12 +274,13 @@ module Archive
233
274
  # argument. Also, the tar program is hard coded to 'tar xf'.
234
275
  #
235
276
  def self.extract_archive(archive, *files)
236
- cmd = "tar xf #{archive}"
237
- cmd = "#{cmd} #{files.join(' ')}" unless files.empty?
277
+ cmd = ['tar', 'xf', archive] + files
278
+
279
+ stdout, stderr, status = Open3.capture3(*cmd)
238
280
 
239
- Open3.popen3(cmd) do |_ain, _aout, aerr|
240
- err = aerr.gets
241
- raise Error, err.chomp if err
281
+ unless status.success?
282
+ error_msg = stderr.empty? ? "Failed to extract archive" : stderr.strip
283
+ raise Error, error_msg
242
284
  end
243
285
 
244
286
  self
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  ###############################################################################
2
4
  # archive_tar_external_spec.rb
3
5
  #
@@ -6,78 +8,88 @@
6
8
  ###############################################################################
7
9
  require 'archive/tar/external'
8
10
  require 'spec_helper'
11
+ require 'fileutils'
9
12
 
10
13
  RSpec.describe Archive::Tar::External do
11
- let(:tmp_file1) { 'temp1.txt' }
12
- let(:tmp_file2) { 'temp2.txt' }
13
- let(:tmp_file3) { 'temp3.txt' }
14
+ let(:first_temp_file) { 'temp1.txt' }
15
+ let(:second_temp_file) { 'temp2.txt' }
16
+ let(:third_temp_file) { 'temp3.txt' }
14
17
 
15
18
  let(:tar_name) { 'test.tar' }
16
- let(:tar_obj) { Archive::Tar::External.new(tar_name) }
19
+ let(:tar_obj) { described_class.new(tar_name) }
17
20
  let(:pattern) { '*.txt' }
18
- let(:gtar) { File.basename(File.which('gtar')) }
19
21
 
20
22
  let(:archive_name) { 'test.tar.gz' }
21
- let(:tar_program) { gtar || 'tar' }
23
+ let(:tar_program) { 'tar' }
22
24
 
23
25
  before do
24
- tar_obj.tar_program = 'gtar' if File.which('gtar')
25
- File.open(tmp_file1, 'w'){ |f| f.puts 'This is a temporary text file' }
26
- File.open(tmp_file2, 'w'){ |f| f.puts 'This is a temporary text file' }
27
- File.open(tmp_file3, 'w'){ |f| f.puts 'This is a temporary text file' }
26
+ File.open(first_temp_file, 'w'){ |f| f.puts 'This is a temporary text file' }
27
+ File.open(second_temp_file, 'w'){ |f| f.puts 'This is a temporary text file' }
28
+ File.open(third_temp_file, 'w'){ |f| f.puts 'This is a temporary text file' }
28
29
  end
29
30
 
30
- example "version" do
31
- expect(Archive::Tar::External::VERSION).to eq('1.4.2')
31
+ after do
32
+ FileUtils.rm_f(first_temp_file)
33
+ FileUtils.rm_f(second_temp_file)
34
+ FileUtils.rm_f(third_temp_file)
35
+
36
+ FileUtils.rm_f(tar_name)
37
+ FileUtils.rm_f("#{tar_name}.gz")
38
+ FileUtils.rm_f("#{tar_name}.bz2")
39
+ FileUtils.rm_f("#{tar_name}.zip")
40
+ end
41
+
42
+ example 'version' do
43
+ expect(Archive::Tar::External::VERSION).to eq('1.6.0')
32
44
  expect(Archive::Tar::External::VERSION).to be_frozen
33
45
  end
34
46
 
35
- context "constructor" do
36
- example "with name" do
37
- expect{ Archive::Tar::External.new(tar_name) }.not_to raise_error
47
+ context 'constructor' do
48
+ example 'with name' do
49
+ expect{ described_class.new(tar_name) }.not_to raise_error
38
50
  end
39
51
 
40
- example "with name and extension" do
41
- expect{ Archive::Tar::External.new(tar_name, pattern) }.not_to raise_error
52
+ example 'with name and extension' do
53
+ expect{ described_class.new(tar_name, pattern) }.not_to raise_error
42
54
  end
43
55
 
44
- example "with compression program", :gzip => true do
45
- expect{ Archive::Tar::External.new(tar_name, pattern, 'gzip') }.not_to raise_error
56
+ example 'with compression program', :gzip do
57
+ expect{ described_class.new(tar_name, pattern, 'gzip') }.not_to raise_error
46
58
  end
47
59
 
48
- example "raises an error if name is not provided" do
49
- expect{ Archive::Tar::External.new }.to raise_error(ArgumentError)
60
+ example 'raises an error if name is not provided' do
61
+ expect{ described_class.new }.to raise_error(ArgumentError)
50
62
  end
51
63
  end
52
64
 
53
- context "instance methods" do
54
- example "tar_program getter" do
65
+ context 'instance methods' do
66
+ example 'tar_program getter' do
55
67
  expect(tar_obj).to respond_to(:tar_program)
56
68
  expect(tar_obj.tar_program).to eq(tar_program)
57
69
  end
58
70
 
59
- example "archive_name getter" do
71
+ example 'archive_name getter' do
60
72
  expect(tar_obj).to respond_to(:archive_name)
61
73
  expect(tar_obj.archive_name).to eq(tar_name)
62
74
  end
63
75
 
64
- example "archive_name setter" do
76
+ example 'archive_name setter' do
65
77
  expect(tar_obj).to respond_to(:archive_name=)
66
78
  expect{ tar_obj.archive_name = 'foo' }.not_to raise_error
67
79
  expect(tar_obj.archive_name).to eq('foo')
68
80
  end
69
81
 
70
- example "compressed_archive_name getter" do
82
+ example 'compressed_archive_name getter' do
71
83
  expect(tar_obj).to respond_to(:compressed_archive_name)
72
84
  expect(tar_obj.compressed_archive_name).to be_nil
73
85
  end
74
86
 
75
- example "compressed_archive_name setter basic functionality" do
87
+ example 'compressed_archive_name setter basic functionality' do
76
88
  expect(tar_obj).to respond_to(:compressed_archive_name=)
77
89
  expect{ tar_obj.compressed_archive_name = archive_name }.not_to raise_error
78
90
  end
79
91
 
80
- example "setting the compressed_archive_name also sets the archive name to the expected value" do
92
+ example 'setting the compressed_archive_name also sets the archive name to the expected value' do
81
93
  tar_obj.compressed_archive_name = archive_name
82
94
  expect(tar_obj.compressed_archive_name).to eq(archive_name)
83
95
  expect(tar_obj.archive_name).to eq(tar_name)
@@ -87,112 +99,117 @@ RSpec.describe Archive::Tar::External do
87
99
  expect(tar_obj.archive_name).to eq(tar_name)
88
100
  end
89
101
 
90
- example "create_archive basic functionality" do
102
+ example 'create_archive basic functionality' do
91
103
  expect(tar_obj).to respond_to(:create_archive)
92
104
  expect{ tar_obj.create_archive(pattern) }.not_to raise_error
93
- expect(File.exist?(tar_name)).to be true
105
+ expect(File.exist?(tar_name)).to be(true)
94
106
  end
95
107
 
96
- example "create_archive requires at least on argument" do
108
+ example 'create_archive requires at least on argument' do
97
109
  expect{ tar_obj.create_archive }.to raise_error(ArgumentError)
98
110
  end
99
111
 
100
- example "create_archive raises an error if no files match the pattern" do
112
+ example 'create_archive raises an error if no files match the pattern' do
101
113
  expect{ tar_obj.create_archive('*.blah') }.to raise_error(Archive::Tar::Error)
102
114
  end
103
115
 
104
- example "create_archive accepts optional parameters" do
105
- expect{ tar_obj.create_archive(pattern, 'cfj') }.not_to raise_error
116
+ example 'create_archive accepts optional parameters' do
117
+ expect{ tar_obj.create_archive(pattern, 'jcf') }.not_to raise_error
106
118
  end
107
119
 
108
- example "create is an alias for create_archive" do
120
+ example 'create is an alias for create_archive' do
109
121
  expect(tar_obj).to respond_to(:create)
110
122
  expect(tar_obj.method(:create)).to eq(tar_obj.method(:create_archive))
111
123
  end
124
+
125
+ example 'format getter' do
126
+ expect(tar_obj).to respond_to(:format)
127
+ expect(tar_obj.format).to eq('pax')
128
+ end
112
129
  end
113
130
 
114
- context "compression" do
115
- example "compress_archive basic functionality" do
131
+ context 'compression' do
132
+ example 'compress_archive basic functionality' do
116
133
  expect(tar_obj).to respond_to(:compress_archive)
117
134
  end
118
135
 
119
- example "compress is an alias for compress_archive" do
136
+ example 'compress is an alias for compress_archive' do
120
137
  expect(tar_obj).to respond_to(:compress)
121
138
  expect(tar_obj.method(:compress)).to eq(tar_obj.method(:compress_archive))
122
139
  end
123
140
 
124
- example "compress_archive defaults to gzip", :gzip => true do
141
+ example 'compress_archive defaults to gzip', :gzip do
125
142
  tar_obj.create_archive(pattern)
126
143
  tar_obj.compress_archive
127
144
 
128
145
  expect(tar_obj.compressed_archive_name).to eq(archive_name)
129
- expect(File.exist?(archive_name)).to be true
146
+ expect(File.exist?(archive_name)).to be(true)
130
147
  end
131
148
 
132
- example "compress_archive works with bzip2", :bzip2 => true do
149
+ example 'compress_archive works with bzip2', :bzip2 do
133
150
  expect{ tar_obj.create_archive(pattern) }.not_to raise_error
134
151
  expect{ tar_obj.compress_archive('bzip2') }.not_to raise_error
135
- expect(File.exist?('test.tar.bz2')).to be true
152
+ expect(File.exist?('test.tar.bz2')).to be(true)
136
153
  end
137
154
  end
138
155
 
139
- context "uncompression" do
156
+ context 'uncompression' do
140
157
  before do
141
158
  tar_obj.create_archive(pattern).compress_archive
142
159
  end
143
160
 
144
- example "uncompress_archive basic functionality" do
161
+ example 'uncompress_archive basic functionality' do
145
162
  expect(tar_obj).to respond_to(:uncompress_archive)
146
163
  end
147
164
 
148
- example "uncompress_archive behaves as expected" do
165
+ example 'uncompress_archive behaves as expected' do
149
166
  expect{ tar_obj.uncompress_archive }.not_to raise_error
150
167
  expect(File.exist?(archive_name)).to be false
151
168
  end
152
169
 
153
- example "uncompress is an alias for uncompress_archive" do
170
+ example 'uncompress is an alias for uncompress_archive' do
154
171
  expect(tar_obj).to respond_to(:uncompress)
155
- expect(tar_obj.method(:uncompress)).to eq (tar_obj.method(:uncompress_archive))
172
+ expect(tar_obj.method(:uncompress)).to eq(tar_obj.method(:uncompress_archive))
156
173
  end
157
174
 
158
- example "uncompress_archive singleton method" do
159
- expect(Archive::Tar::External).to respond_to(:uncompress_archive)
175
+ example 'uncompress_archive singleton method' do
176
+ expect(described_class).to respond_to(:uncompress_archive)
160
177
  end
161
178
  end
162
179
 
163
- context "archive" do
164
- example "archive_info basic functionality" do
180
+ context 'archive' do
181
+ example 'archive_info basic functionality' do
165
182
  expect(tar_obj).to respond_to(:archive_info)
166
183
  end
167
184
 
168
- example "archive_info returns the expected value" do
185
+ example 'archive_info returns the expected value' do
169
186
  tar_obj.create_archive(pattern)
170
- expect(tar_obj.archive_info).to eq([tmp_file1, tmp_file2, tmp_file3])
187
+ expect(tar_obj.archive_info).to eq([first_temp_file, second_temp_file, third_temp_file])
171
188
  end
172
189
 
173
- example "add_to_archive basic functionality" do
190
+ example 'add_to_archive basic functionality' do
174
191
  expect(tar_obj).to respond_to(:add_to_archive)
175
192
  end
176
193
 
177
- example "add_to_archive works as expected" do
194
+ example 'add_to_archive works as expected' do
178
195
  tar_obj = described_class.new(tar_name)
179
- expect{ tar_obj.add_to_archive(tmp_file2) }.not_to raise_error
180
- expect{ tar_obj.add_to_archive(tmp_file2, tmp_file3) }.not_to raise_error
181
- expect(tar_obj.archive_info).to eq([tmp_file2, tmp_file2, tmp_file3])
196
+ expect{ tar_obj.add_to_archive(second_temp_file) }.not_to raise_error
197
+ expect{ tar_obj.add_to_archive(second_temp_file, third_temp_file) }.not_to raise_error
198
+ expect(tar_obj.archive_info).to eq([second_temp_file, second_temp_file, third_temp_file])
182
199
  end
183
200
 
184
- example "update_archive basic functionality" do
201
+ example 'update_archive basic functionality' do
185
202
  expect(tar_obj).to respond_to(:update_archive)
186
203
  end
187
204
 
188
- example "update_archive behaves as expected", :gtar => true do
205
+ example 'update_archive behaves as expected' do
189
206
  tar_obj.create_archive(pattern)
190
- expect(tar_obj.archive_info).to eq([tmp_file1, tmp_file2, tmp_file3])
191
- tar_obj.update_archive(tmp_file2)
192
- expect(tar_obj.archive_info).to eq([tmp_file1, tmp_file2, tmp_file3])
207
+ expect(tar_obj.archive_info).to eq([first_temp_file, second_temp_file, third_temp_file])
208
+ tar_obj.update_archive(second_temp_file)
209
+ expect(tar_obj.archive_info).to eq([first_temp_file, second_temp_file, third_temp_file])
193
210
  end
194
211
 
195
- example "extract_archive_basic" do
212
+ example 'extract_archive_basic' do
196
213
  expect(tar_obj).to respond_to(:extract_archive)
197
214
  end
198
215
 
@@ -201,31 +218,20 @@ RSpec.describe Archive::Tar::External do
201
218
  expect{ tar_obj.expand('blah.txt') }.to raise_error(Archive::Tar::Error)
202
219
  end
203
220
 
204
- example "extract_archive with no arguments extracts all files" do
221
+ example 'extract_archive with no arguments extracts all files' do
205
222
  tar_obj.create(pattern)
206
223
  expect{ tar_obj.extract_archive }.not_to raise_error
207
224
  end
208
225
 
209
- example "extract_archive with a valid file argument behaves as expected" do
226
+ example 'extract_archive with a valid file argument behaves as expected' do
210
227
  tar_obj.create(pattern)
211
- expect{ tar_obj.extract_archive(tmp_file2) }.not_to raise_error
228
+ expect{ tar_obj.extract_archive(second_temp_file) }.not_to raise_error
212
229
  end
213
230
 
214
- example "expand_archive, expand and extract are aliases for extract_archive" do
231
+ example 'expand_archive, expand and extract are aliases for extract_archive' do
215
232
  expect(tar_obj.method(:expand_archive)).to eq(tar_obj.method(:extract_archive))
216
233
  expect(tar_obj.method(:expand)).to eq(tar_obj.method(:extract_archive))
217
234
  expect(tar_obj.method(:extract)).to eq(tar_obj.method(:extract_archive))
218
235
  end
219
236
  end
220
-
221
- after do
222
- File.delete(tmp_file1) if File.exist?(tmp_file1)
223
- File.delete(tmp_file2) if File.exist?(tmp_file2)
224
- File.delete(tmp_file3) if File.exist?(tmp_file3)
225
-
226
- File.delete(tar_name) if File.exist?(tar_name)
227
- File.delete("#{tar_name}.gz") if File.exist?("#{tar_name}.gz")
228
- File.delete("#{tar_name}.bz2") if File.exist?("#{tar_name}.bz2")
229
- File.delete("#{tar_name}.zip") if File.exist?("#{tar_name}.zip")
230
- end
231
237
  end
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rspec'
2
4
  require 'ptools'
3
5
 
4
6
  RSpec.configure do |config|
5
7
  config.filter_run_excluding(:gzip) unless File.which('gzip')
6
- config.filter_run_excluding(:gtar) unless File.which('gtar')
7
8
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: archive-tar-external
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.2
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Berger
@@ -35,7 +35,7 @@ cert_chain:
35
35
  ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM
36
36
  WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh
37
37
  -----END CERTIFICATE-----
38
- date: 2021-04-30 00:00:00.000000000 Z
38
+ date: 2025-07-17 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: rake
@@ -71,14 +71,42 @@ dependencies:
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '1.4'
74
+ version: '1.5'
75
75
  type: :development
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '1.4'
81
+ version: '1.5'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rubocop
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rubocop-rspec
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
82
110
  description: |2
83
111
  The archive-tar-external is a simple wrapper interface for creating
84
112
  tar files using your system's tar command. You can also easily compress
@@ -97,6 +125,7 @@ files:
97
125
  - Rakefile
98
126
  - archive-tar-external.gemspec
99
127
  - certs/djberg96_pub.pem
128
+ - doc/IMPROVEMENTS.md
100
129
  - doc/archive_tar_external.md
101
130
  - lib/archive-tar-external.rb
102
131
  - lib/archive/tar/external.rb
@@ -108,10 +137,13 @@ licenses:
108
137
  metadata:
109
138
  homepage_uri: https://github.com/djberg96/archive-tar-external
110
139
  bug_tracker_uri: https://github.com/djberg96/archive-tar-external/issues
111
- changelog_uri: https://github.com/djberg96/archive-tar-external/blob/main/CHANGES
140
+ changelog_uri: https://github.com/djberg96/archive-tar-external/blob/main/CHANGES.md
112
141
  documentation_uri: https://github.com/djberg96/archive-tar-external/wiki
113
142
  source_code_uri: https://github.com/djberg96/archive-tar-external
114
143
  wiki_uri: https://github.com/djberg96/archive-tar-external/wiki
144
+ rubygems_mfa_required: 'true'
145
+ funding_uri: https://github.com/sponsors/djberg96
146
+ github_repo: https://github.com/djberg96/archive-tar-external
115
147
  post_install_message:
116
148
  rdoc_options: []
117
149
  require_paths:
@@ -127,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
159
  - !ruby/object:Gem::Version
128
160
  version: '0'
129
161
  requirements: []
130
- rubygems_version: 3.2.15
162
+ rubygems_version: 3.5.22
131
163
  signing_key:
132
164
  specification_version: 4
133
165
  summary: A simple way to create tar archives using external calls
metadata.gz.sig CHANGED
Binary file