omnizip 0.3.31 → 0.3.33
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 +30 -0
- data/lib/omnizip/archive_handler.rb +1 -0
- data/lib/omnizip/archive_handlers/seven_zip_handler.rb +58 -0
- data/lib/omnizip/archive_handlers.rb +1 -0
- data/lib/omnizip/cli.rb +32 -3
- data/lib/omnizip/convenience.rb +41 -7
- data/lib/omnizip/version.rb +1 -1
- data/lib/omnizip.rb +1 -0
- metadata +2 -2
- data/lib/omnizip/cli/shared.rb +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0f3064a982a38257ff4ef606d6de52925a377e2138a3bacc7cb60033a6ff1ac1
|
|
4
|
+
data.tar.gz: f24f28f6daac8df45542854f099c071d5b17f52deff99ae7f3bd5d009d6d7c0d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7e08082742ca10c5156773d5d279c1e5c7fe6d035cbe06678e2c68509862d89880c1a7f3e40d4e7a69b433e2407546d3735846a7d53a3c139819fa271001b852
|
|
7
|
+
data.tar.gz: 8d8551cf4cd26ae177b36e5aa67f217323853535ffaed26e4825f537e4384e998629b6084240a2dd82eaad276396dd51b22ab84a4d65f6f9f98475965ca6096e
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Fixed
|
|
11
|
+
- The convenience API (`compress_file`, `compress_directory`,
|
|
12
|
+
`extract_archive`, `list_archive`, `read_from_archive`,
|
|
13
|
+
`add_to_archive`, `remove_from_archive`) now routes archive
|
|
14
|
+
formats by extension: `.tar` writes/reads real TAR (previously a
|
|
15
|
+
mislabeled ZIP), `.7z` writes/reads real 7z via a new
|
|
16
|
+
`ArchiveHandlers::SevenZipHandler` (previously a mislabeled ZIP;
|
|
17
|
+
archives verify with `7zz`), and read-only formats (`.rar`,
|
|
18
|
+
`.iso`, `.cpio`) raise `UnsupportedFormatError` instead of
|
|
19
|
+
silently writing a ZIP under a foreign name. Extensionless
|
|
20
|
+
outputs keep the ZIP default; an explicit `format:` still wins.
|
|
21
|
+
|
|
22
|
+
## [0.3.32] - 2026-08-27
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- The `omnizip` executable crashed on every invocation
|
|
26
|
+
(`uninitialized constant Omnizip::Cli`): the `Cli` autoload was
|
|
27
|
+
missing and the Thor command classes referenced
|
|
28
|
+
`Omnizip::Cli::Shared` mid-load, re-triggering the in-progress
|
|
29
|
+
autoload. The autoload exists now and Shared is defined ahead of
|
|
30
|
+
the command classes in `lib/omnizip/cli.rb` (its separate file is
|
|
31
|
+
gone). Verified: `omnizip version`, the documented
|
|
32
|
+
`compress/decompress output.lzma` round-trip (which also
|
|
33
|
+
interops with `xz --format=lzma` in both directions), and
|
|
34
|
+
`archive create backup.7z` producing an archive `7zz` validates.
|
|
35
|
+
|
|
36
|
+
## [0.3.31] - 2026-08-27
|
|
37
|
+
|
|
10
38
|
### Fixed
|
|
11
39
|
- `Omnizip.compress_file` now routes single-file compression by
|
|
12
40
|
output extension: `.gz`, `.bz2`, `.xz`, `.lzma` and `.lz` produce
|
|
@@ -15,6 +43,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
15
43
|
`compress_file('input.txt', 'output.lzma', algorithm: :lzma)`
|
|
16
44
|
example previously emitted `PK`-magic data). Unknown extensions
|
|
17
45
|
and explicit archive formats keep the previous behavior.
|
|
46
|
+
- CI: the Windows matrix no longer fails when choco's winrar
|
|
47
|
+
download 404s (RAR specs skip when it is absent).
|
|
18
48
|
|
|
19
49
|
## [0.3.30] - 2026-08-27
|
|
20
50
|
|
|
@@ -69,6 +69,7 @@ module Omnizip
|
|
|
69
69
|
LAZY_LOAD_TRIGGERS = {
|
|
70
70
|
zip: -> { Omnizip::ArchiveHandlers::ZipHandler },
|
|
71
71
|
tar: -> { Omnizip::ArchiveHandlers::TarHandler },
|
|
72
|
+
seven_zip: -> { Omnizip::ArchiveHandlers::SevenZipHandler },
|
|
72
73
|
}.freeze
|
|
73
74
|
private_constant :LAZY_LOAD_TRIGGERS
|
|
74
75
|
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module ArchiveHandlers
|
|
5
|
+
# Adapter that exposes the canonical +create+/+extract_to+/+list+
|
|
6
|
+
# interface for the 7z format. Wraps +Omnizip::Formats::SevenZip+.
|
|
7
|
+
class SevenZipHandler
|
|
8
|
+
# Creates a .7z archive. The yielded proxy only supports
|
|
9
|
+
# +add(name, source_path)+ for files — directory entries are not
|
|
10
|
+
# part of the single-file convenience flow.
|
|
11
|
+
def create(path, &block)
|
|
12
|
+
Omnizip::Formats::SevenZip.create(path) do |writer|
|
|
13
|
+
block&.call(FileProxy.new(writer))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def extract_to(path, output_dir, **options)
|
|
18
|
+
Omnizip::Formats::SevenZip.open(path, options) do |reader|
|
|
19
|
+
reader.extract_all(output_dir)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def list(path, details: false, **options)
|
|
24
|
+
Omnizip::Formats::SevenZip.open(path, options) do |reader|
|
|
25
|
+
files = reader.list_files
|
|
26
|
+
return details ? files.map { |f| { name: f } } : files
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def read_entry(_path, _entry_name)
|
|
31
|
+
raise NotImplementedError,
|
|
32
|
+
"read_from_archive for .7z is not wired; use " \
|
|
33
|
+
"Formats::SevenZip.open"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Translates the generic +add(name, source_path)+ interface to
|
|
37
|
+
# the 7z writer's +add_file(file_path, archive_path)+.
|
|
38
|
+
class FileProxy
|
|
39
|
+
def initialize(writer)
|
|
40
|
+
@writer = writer
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def add(name, source_path = nil)
|
|
44
|
+
if source_path.nil?
|
|
45
|
+
raise ArgumentError,
|
|
46
|
+
"directory entries are not supported in .7z " \
|
|
47
|
+
"convenience compression"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@writer.add_file(source_path, name)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Omnizip::ArchiveHandler.register(:seven_zip,
|
|
58
|
+
Omnizip::ArchiveHandlers::SevenZipHandler.new)
|
data/lib/omnizip/cli.rb
CHANGED
|
@@ -19,9 +19,38 @@
|
|
|
19
19
|
require "thor"
|
|
20
20
|
|
|
21
21
|
module Omnizip
|
|
22
|
+
# Shared helpers used by the Thor command groups defined below
|
|
23
|
+
# (ProfileCommands, ArchiveCommands, Cli). Defined here because the
|
|
24
|
+
# command classes include it at class-body time; a separate file
|
|
25
|
+
# under Omnizip::Cli would re-trigger this file's autoload
|
|
26
|
+
# mid-load.
|
|
27
|
+
module Shared
|
|
28
|
+
# Print a formatted error message and exit nonzero.
|
|
29
|
+
#
|
|
30
|
+
# @param error [StandardError] the error to display
|
|
31
|
+
def handle_error(error)
|
|
32
|
+
warn Omnizip::CliOutputFormatter.format_error(error)
|
|
33
|
+
exit 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Format a byte count with a binary-unit suffix.
|
|
37
|
+
#
|
|
38
|
+
# @param bytes [Integer] the byte count
|
|
39
|
+
# @return [String] human-readable size (e.g. "1.5 MB")
|
|
40
|
+
def format_bytes(bytes)
|
|
41
|
+
return "0 B" if bytes.zero?
|
|
42
|
+
|
|
43
|
+
units = %w[B KB MB GB TB]
|
|
44
|
+
exp = (Math.log(bytes) / Math.log(1024)).to_i
|
|
45
|
+
exp = [exp, units.size - 1].min
|
|
46
|
+
|
|
47
|
+
"%.1f %s" % [bytes.to_f / (1024**exp), units[exp]]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
22
51
|
# Profile commands subcommand group
|
|
23
52
|
class ProfileCommands < Thor
|
|
24
|
-
include
|
|
53
|
+
include Shared
|
|
25
54
|
|
|
26
55
|
class << self
|
|
27
56
|
def exit_on_failure?
|
|
@@ -69,7 +98,7 @@ module Omnizip
|
|
|
69
98
|
|
|
70
99
|
# Archive commands subcommand group
|
|
71
100
|
class ArchiveCommands < Thor
|
|
72
|
-
include
|
|
101
|
+
include Shared
|
|
73
102
|
|
|
74
103
|
class << self
|
|
75
104
|
def exit_on_failure?
|
|
@@ -298,7 +327,7 @@ module Omnizip
|
|
|
298
327
|
# Provides Thor-based CLI commands for compressing and decompressing
|
|
299
328
|
# files using various compression algorithms.
|
|
300
329
|
class Cli < Thor
|
|
301
|
-
include
|
|
330
|
+
include Shared
|
|
302
331
|
|
|
303
332
|
class << self
|
|
304
333
|
def exit_on_failure?
|
data/lib/omnizip/convenience.rb
CHANGED
|
@@ -40,7 +40,7 @@ module Omnizip
|
|
|
40
40
|
return output_path
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
Omnizip::ArchiveHandler.for(format).create(output_path) do |archive|
|
|
43
|
+
Omnizip::ArchiveHandler.for(resolve_archive_format(output_path, format)).create(output_path) do |archive|
|
|
44
44
|
archive.add(::File.basename(input_path), input_path)
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -69,7 +69,7 @@ module Omnizip
|
|
|
69
69
|
apply_profile(first_file, options)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
Omnizip::ArchiveHandler.for(format).create(output_path) do |archive|
|
|
72
|
+
Omnizip::ArchiveHandler.for(resolve_archive_format(output_path, format)).create(output_path) do |archive|
|
|
73
73
|
add_directory_contents(archive, input_dir, "", recursive: recursive)
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -87,7 +87,7 @@ module Omnizip
|
|
|
87
87
|
def extract_archive(archive_path, output_dir, format: DEFAULT_FORMAT,
|
|
88
88
|
overwrite: false, **options)
|
|
89
89
|
require_archive!(archive_path)
|
|
90
|
-
Omnizip::ArchiveHandler.for(format)
|
|
90
|
+
Omnizip::ArchiveHandler.for(resolve_archive_format(archive_path, format))
|
|
91
91
|
.extract_to(archive_path, output_dir,
|
|
92
92
|
overwrite: overwrite, **options)
|
|
93
93
|
end
|
|
@@ -102,7 +102,7 @@ module Omnizip
|
|
|
102
102
|
def list_archive(archive_path, format: DEFAULT_FORMAT, details: false,
|
|
103
103
|
**options)
|
|
104
104
|
require_archive!(archive_path)
|
|
105
|
-
Omnizip::ArchiveHandler.for(format)
|
|
105
|
+
Omnizip::ArchiveHandler.for(resolve_archive_format(archive_path, format))
|
|
106
106
|
.list(archive_path, details: details, **options)
|
|
107
107
|
end
|
|
108
108
|
|
|
@@ -114,7 +114,7 @@ module Omnizip
|
|
|
114
114
|
# @return [String] Entry contents
|
|
115
115
|
def read_from_archive(archive_path, entry_name, format: DEFAULT_FORMAT)
|
|
116
116
|
require_archive!(archive_path)
|
|
117
|
-
Omnizip::ArchiveHandler.for(format).read_entry(archive_path, entry_name)
|
|
117
|
+
Omnizip::ArchiveHandler.for(resolve_archive_format(archive_path, format)).read_entry(archive_path, entry_name)
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
# Add a file to an existing archive.
|
|
@@ -131,7 +131,7 @@ module Omnizip
|
|
|
131
131
|
raise Errno::ENOENT, "Source file not found: #{source_path}"
|
|
132
132
|
end
|
|
133
133
|
|
|
134
|
-
handler = Omnizip::ArchiveHandler.for(format)
|
|
134
|
+
handler = Omnizip::ArchiveHandler.for(resolve_archive_format(archive_path, format))
|
|
135
135
|
# allowed: handler is a registered duck; missing add_entry raises below
|
|
136
136
|
if handler.respond_to?(:add_entry)
|
|
137
137
|
handler.add_entry(archive_path, entry_name, source_path)
|
|
@@ -151,7 +151,7 @@ module Omnizip
|
|
|
151
151
|
# @return [String] +archive_path+
|
|
152
152
|
def remove_from_archive(archive_path, entry_name, format: DEFAULT_FORMAT)
|
|
153
153
|
require_archive!(archive_path)
|
|
154
|
-
handler = Omnizip::ArchiveHandler.for(format)
|
|
154
|
+
handler = Omnizip::ArchiveHandler.for(resolve_archive_format(archive_path, format))
|
|
155
155
|
# allowed: handler is a registered duck; missing remove_entry raises below
|
|
156
156
|
unless handler.respond_to?(:remove_entry)
|
|
157
157
|
raise Omnizip::UnsupportedFormatError,
|
|
@@ -172,6 +172,20 @@ module Omnizip
|
|
|
172
172
|
# Extension -> single-file compressor map. Each lambda takes
|
|
173
173
|
# (input_path, output_path, options). These formats are streams,
|
|
174
174
|
# not archives, so they bypass ArchiveHandler.
|
|
175
|
+
# Extensions naming archive formats with a registered handler;
|
|
176
|
+
# routed when the caller did not pass an explicit +format:+.
|
|
177
|
+
ARCHIVE_FORMAT_EXTENSIONS = {
|
|
178
|
+
".zip" => :zip,
|
|
179
|
+
".tar" => :tar,
|
|
180
|
+
".7z" => :seven_zip,
|
|
181
|
+
}.freeze
|
|
182
|
+
|
|
183
|
+
# Extensions naming real formats this gem can READ but not write
|
|
184
|
+
# through the convenience API. Writing them a ZIP under a foreign
|
|
185
|
+
# name was silent corruption; failing truthfully is the only
|
|
186
|
+
# honest behavior.
|
|
187
|
+
READ_ONLY_FORMAT_EXTENSIONS = [".rar", ".iso", ".cpio"].freeze
|
|
188
|
+
|
|
175
189
|
SINGLE_FILE_COMPRESSORS = {
|
|
176
190
|
".gz" => lambda do |input, output, options|
|
|
177
191
|
Omnizip::Formats::Gzip.compress(input, output, options)
|
|
@@ -206,6 +220,26 @@ module Omnizip
|
|
|
206
220
|
SINGLE_FILE_COMPRESSORS[ext]
|
|
207
221
|
end
|
|
208
222
|
|
|
223
|
+
# Archive format for a path: an explicit +format+ wins unless it
|
|
224
|
+
# is the default; otherwise the extension routes. Known-but-
|
|
225
|
+
# unwritable extensions raise instead of receiving a mislabeled
|
|
226
|
+
# ZIP.
|
|
227
|
+
def resolve_archive_format(path, format)
|
|
228
|
+
return format unless format == DEFAULT_FORMAT
|
|
229
|
+
|
|
230
|
+
ext = ::File.extname(path).downcase
|
|
231
|
+
routed = ARCHIVE_FORMAT_EXTENSIONS[ext]
|
|
232
|
+
return routed if routed
|
|
233
|
+
|
|
234
|
+
if READ_ONLY_FORMAT_EXTENSIONS.include?(ext)
|
|
235
|
+
raise Omnizip::UnsupportedFormatError,
|
|
236
|
+
"#{ext} archives cannot be written by the convenience " \
|
|
237
|
+
"API (read-only support); use the format-specific reader"
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
DEFAULT_FORMAT
|
|
241
|
+
end
|
|
242
|
+
|
|
209
243
|
private
|
|
210
244
|
|
|
211
245
|
def require_archive!(archive_path)
|
data/lib/omnizip/version.rb
CHANGED
data/lib/omnizip.rb
CHANGED
|
@@ -67,6 +67,7 @@ module Omnizip
|
|
|
67
67
|
autoload :Profiler, "omnizip/profiler"
|
|
68
68
|
autoload :Commands, "omnizip/commands"
|
|
69
69
|
autoload :CliOutputFormatter, "omnizip/cli/output_formatter"
|
|
70
|
+
autoload :Cli, "omnizip/cli"
|
|
70
71
|
|
|
71
72
|
# Implementation namespaces. Each namespace file declares autoloads
|
|
72
73
|
# for its concrete classes. The classes self-register with their
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omnizip
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.33
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -321,6 +321,7 @@ files:
|
|
|
321
321
|
- lib/omnizip/algorithms/zstandard/xxhash.rb
|
|
322
322
|
- lib/omnizip/archive_handler.rb
|
|
323
323
|
- lib/omnizip/archive_handlers.rb
|
|
324
|
+
- lib/omnizip/archive_handlers/seven_zip_handler.rb
|
|
324
325
|
- lib/omnizip/archive_handlers/tar_handler.rb
|
|
325
326
|
- lib/omnizip/archive_handlers/zip_handler.rb
|
|
326
327
|
- lib/omnizip/buffer.rb
|
|
@@ -339,7 +340,6 @@ files:
|
|
|
339
340
|
- lib/omnizip/chunked/writer.rb
|
|
340
341
|
- lib/omnizip/cli.rb
|
|
341
342
|
- lib/omnizip/cli/output_formatter.rb
|
|
342
|
-
- lib/omnizip/cli/shared.rb
|
|
343
343
|
- lib/omnizip/commands.rb
|
|
344
344
|
- lib/omnizip/commands/.keep
|
|
345
345
|
- lib/omnizip/commands/archive_create_command.rb
|
data/lib/omnizip/cli/shared.rb
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Omnizip
|
|
4
|
-
class Cli
|
|
5
|
-
# Shared helpers used by the Thor command groups defined in
|
|
6
|
-
# lib/omnizip/cli.rb (ProfileCommands, ArchiveCommands, Cli).
|
|
7
|
-
module Shared
|
|
8
|
-
# Print a formatted error message and exit nonzero.
|
|
9
|
-
#
|
|
10
|
-
# @param error [StandardError] the error to display
|
|
11
|
-
def handle_error(error)
|
|
12
|
-
warn Omnizip::CliOutputFormatter.format_error(error)
|
|
13
|
-
exit 1
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Format a byte count with a binary-unit suffix.
|
|
17
|
-
#
|
|
18
|
-
# @param bytes [Integer] the byte count
|
|
19
|
-
# @return [String] human-readable size (e.g. "1.5 MB")
|
|
20
|
-
def format_bytes(bytes)
|
|
21
|
-
return "0 B" if bytes.zero?
|
|
22
|
-
|
|
23
|
-
units = %w[B KB MB GB TB]
|
|
24
|
-
exp = (Math.log(bytes) / Math.log(1024)).to_i
|
|
25
|
-
exp = [exp, units.size - 1].min
|
|
26
|
-
|
|
27
|
-
"%.1f %s" % [bytes.to_f / (1024**exp), units[exp]]
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|