omnizip 0.4.7 → 0.4.9

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: f958a6bde5f52729231c66fcc680ecaf30bd3bb3e3a04f59bd843a63f5d664ec
4
- data.tar.gz: c35f940965b7776bbab14f41258917bf4ae444899ec70d4c0713a36a12d5c05b
3
+ metadata.gz: 11fd728c2550d26fff721df376386c0f1b33bee6a025895770b214c0fe49e7a0
4
+ data.tar.gz: fa48858d122fb3a16460681678fa8db9f96382c2a5272dfab90b73b128a9baca
5
5
  SHA512:
6
- metadata.gz: 6e219d9d7052b667c112709d0ebaa4a4e3b6925e374694f1fd646114e6ab7d2157e255a07a8e05b9a42a9234b4e61841617ce4682b6e79727ee6a4381a4e2b35
7
- data.tar.gz: f6921e7de8606e19dd28e62d4a00a8bec55b2f0bc6f4a4478805df6299f88b912aa6b8ea56fbf774f6df42a7f29cc707d709c4dc250c702a5d3b4ca91d785601
6
+ metadata.gz: b30e1ee39bd3eef72f02c06ae5a4fe10e84c3b71dc7efb5528593c8f092129572b43a6ca1e1a80469d528b8bc5692a3ef3bc9aff0e1a57245b2cb5a99017578c
7
+ data.tar.gz: 53b3fd01e1976a9cf6dba5d3c3745cd5a9994be42a98be4b7e6fe95ba60b2ba0282fe2a5a6936a4fa9486d4711b3bdf3b644451b014659c0a35ef80104c7f100
data/.rubocop_todo.yml CHANGED
@@ -138,6 +138,9 @@ Metrics/AbcSize:
138
138
  # AllowedMethods: refine
139
139
  Metrics/BlockLength:
140
140
  Max: 81
141
+ Exclude:
142
+ - "lib/tasks/platform_gems.rake"
143
+ - "spec/omnizip/archive_handlers/rust_tier_handlers_spec.rb"
141
144
 
142
145
  # Offense count: 5
143
146
  # Configuration parameters: CountBlocks, CountModifierForms.
data/CHANGELOG.md CHANGED
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Fixed
11
+ - RPM archive-tier readiness: the dylib requirement is
12
+ **0.21.112**, not 0.21.111 — 0.21.111's `./`-stripping was half the
13
+ fix (the Ruby handler's names are ABSOLUTE, from the header's
14
+ DIRNAMES/BASENAMES; 0.21.112 normalizes the cpio names to
15
+ `/usr/…`). The tier spec version-guards accordingly, and with
16
+ 0.21.112 the rpm tier is fully active (list stays Ruby by design;
17
+ read_entry is accelerated). Ships with the rubocop exclusion for
18
+ the tier spec that a CI-skipped merge dropped.
19
+
20
+ ### Added
21
+ - Archive-level tier for every handler: tar, cpio, iso, xar, 7z and
22
+ rar3/4/5 (with `password:` passthrough) route list(names) and
23
+ read_entry through the cdylib's whole-archive surface, falling back
24
+ to the Ruby readers on any Rust error or a missing library. rpm
25
+ rides the tier for read_entry once the dylib is ≥ 0.21.111 (which
26
+ strips the cpio `./` prefix so its entry names match the Ruby
27
+ handler's); rpm list and OLE stay pure-Ruby — OLE is a pinned model
28
+ divergence (Ruby lists top-level entries, Rust flattens the storage
29
+ tree), documented in the capability matrix and pinned by the new
30
+ cross-implementation differential spec.
31
+
10
32
  ### Added
11
33
  - Prebuilt-binary distribution (parsanol model): platform gems
12
34
  (x86_64/aarch64/arm Linux glibc+musl, arm64/x86_64 macOS,
@@ -20,6 +20,11 @@ module Omnizip
20
20
  end
21
21
 
22
22
  def list(path, details: false, **_)
23
+ unless details
24
+ tier_names = Omnizip::Backends.archive_entry_names(path)
25
+ return tier_names if tier_names
26
+ end
27
+
23
28
  entries = Omnizip::Formats::Cpio.list(path)
24
29
  if details
25
30
  entries.map do |e|
@@ -32,6 +37,9 @@ module Omnizip
32
37
  end
33
38
 
34
39
  def read_entry(path, entry_name, **_)
40
+ tier = Omnizip::Backends.archive_read_entry(path, entry_name)
41
+ return tier unless tier.nil?
42
+
35
43
  entry = Omnizip::Formats::Cpio.list(path)
36
44
  .find { |e| e.name == entry_name }
37
45
  unless entry
@@ -20,6 +20,11 @@ module Omnizip
20
20
  end
21
21
 
22
22
  def list(path, details: false, **_)
23
+ unless details
24
+ tier_names = Omnizip::Backends.archive_entry_names(path)
25
+ return tier_names if tier_names
26
+ end
27
+
23
28
  entries = Omnizip::Formats::Iso.list(path)
24
29
  if details
25
30
  entries.map do |e|
@@ -32,6 +37,9 @@ module Omnizip
32
37
  end
33
38
 
34
39
  def read_entry(path, entry_name, **_)
40
+ tier = Omnizip::Backends.archive_read_entry(path, entry_name)
41
+ return tier unless tier.nil?
42
+
35
43
  entry = Omnizip::Formats::Iso.list(path)
36
44
  .find { |e| e.full_path == entry_name }
37
45
  unless entry
@@ -29,6 +29,11 @@ module Omnizip
29
29
  end
30
30
 
31
31
  def list(path, details: false, **_)
32
+ unless details
33
+ tier_names = Omnizip::Backends.archive_entry_names(path)
34
+ return tier_names if tier_names
35
+ end
36
+
32
37
  reader = Omnizip::Formats::Rar::Reader.new(path).open
33
38
  entries = reader.list_files
34
39
  if details
@@ -43,6 +48,9 @@ module Omnizip
43
48
  end
44
49
 
45
50
  def read_entry(path, entry_name, password: nil, **_)
51
+ tier = Omnizip::Backends.archive_read_entry(path, entry_name, password: password)
52
+ return tier unless tier.nil?
53
+
46
54
  Dir.mktmpdir("omnizip-rar-entry") do |dir|
47
55
  dest = File.join(dir, "entry")
48
56
  Omnizip::Formats::Rar::Decompressor.extract_entry(
@@ -29,6 +29,9 @@ module Omnizip
29
29
  end
30
30
 
31
31
  def read_entry(path, entry_name, **_)
32
+ tier = Omnizip::Backends.archive_read_entry(path, entry_name)
33
+ return tier unless tier.nil?
34
+
32
35
  Dir.mktmpdir("omnizip-rpm-entry") do |dir|
33
36
  Omnizip::Formats::Rpm.extract(path, dir)
34
37
  dest = File.join(dir, entry_name)
@@ -25,6 +25,11 @@ module Omnizip
25
25
  end
26
26
 
27
27
  def list(path, details: false, **options)
28
+ unless details
29
+ tier_names = Omnizip::Backends.archive_entry_names(path, password: options[:password])
30
+ return tier_names if tier_names
31
+ end
32
+
28
33
  with_reader(path, options) do |reader|
29
34
  entries = reader.list_files
30
35
  if details
@@ -40,6 +45,9 @@ module Omnizip
40
45
  end
41
46
 
42
47
  def read_entry(path, entry_name, **options)
48
+ tier = Omnizip::Backends.archive_read_entry(path, entry_name, password: options[:password])
49
+ return tier unless tier.nil?
50
+
43
51
  with_reader(path, options) do |reader|
44
52
  entry = reader.list_files.find { |e| e.name == entry_name }
45
53
  raise Errno::ENOENT, "Entry not found: #{entry_name}" unless entry
@@ -22,6 +22,11 @@ module Omnizip
22
22
  end
23
23
 
24
24
  def list(path, details: false, **_)
25
+ unless details
26
+ tier_names = Omnizip::Backends.archive_entry_names(path)
27
+ return tier_names if tier_names
28
+ end
29
+
25
30
  entries = Omnizip::Formats::Tar.list(path)
26
31
  if details
27
32
  entries.map do |entry|
@@ -58,7 +63,10 @@ module Omnizip
58
63
  end
59
64
  end
60
65
 
61
- def read_entry(path, entry_name)
66
+ def read_entry(path, entry_name, **_)
67
+ tier = Omnizip::Backends.archive_read_entry(path, entry_name)
68
+ return tier unless tier.nil?
69
+
62
70
  data = nil
63
71
  Omnizip::Formats::Tar.open(path) do |reader|
64
72
  entry = reader.entries.find { |e| e.name == entry_name }
@@ -21,6 +21,11 @@ module Omnizip
21
21
  end
22
22
 
23
23
  def list(path, details: false, **_)
24
+ unless details
25
+ tier_names = Omnizip::Backends.archive_entry_names(path)
26
+ return tier_names if tier_names
27
+ end
28
+
24
29
  entries = Omnizip::Formats::Xar.list(path)
25
30
  if details
26
31
  entries.map do |e|
@@ -33,6 +38,9 @@ module Omnizip
33
38
  end
34
39
 
35
40
  def read_entry(path, entry_name, **_)
41
+ tier = Omnizip::Backends.archive_read_entry(path, entry_name)
42
+ return tier unless tier.nil?
43
+
36
44
  Dir.mktmpdir("omnizip-xar-entry") do |dir|
37
45
  Omnizip::Formats::Xar.extract(path, dir)
38
46
  dest = File.join(dir, entry_name)
@@ -96,15 +96,18 @@ module Omnizip
96
96
 
97
97
  # Whole-archive tier helpers: nil means "Rust unavailable or
98
98
  # entry not found — caller falls back to the Ruby reader".
99
- def archive_entry_names(path)
100
- Omnizip::Implementations::Rust::Archive.open(File.binread(path)) { |a| return a.entry_names }
99
+ # Encrypted formats (rar, 7z) pass password:.
100
+ def archive_entry_names(path, password: nil)
101
+ Omnizip::Implementations::Rust::Archive.open(File.binread(path), password: password) do |a|
102
+ return a.entry_names
103
+ end
101
104
  rescue Omnizip::Implementations::Rust::Archive::LibraryMissing, Omnizip::Implementations::Rust::Error, StandardError
102
105
  nil
103
106
  end
104
107
 
105
108
  # rubocop:disable-next Lint/ReturnInVoidContext
106
- def archive_read_entry(path, entry_name)
107
- Omnizip::Implementations::Rust::Archive.open(File.binread(path)) do |a|
109
+ def archive_read_entry(path, entry_name, password: nil)
110
+ Omnizip::Implementations::Rust::Archive.open(File.binread(path), password: password) do |a|
108
111
  idx = a.entry_names.index(entry_name)
109
112
  return nil if idx.nil?
110
113
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Omnizip
4
- VERSION = "0.4.7"
4
+ VERSION = "0.4.9"
5
5
  end
@@ -42,26 +42,72 @@ namespace :platform_gems do
42
42
  desc "Build platform gems for every tarball in DIR=libomnizip_ffi-<target>.tar.gz"
43
43
  task :build do
44
44
  dir = ENV.fetch("DIR") { abort "DIR=<artifacts dir> required" }
45
+ # Snapshot the pristine file list once. Gem::Specification.load
46
+ # caches by path and returns the SAME object on every call, so a
47
+ # per-call load + files += would leak the previous platform's
48
+ # cdylib into the next gem.
49
+ gem_root = File.expand_path("../..", __dir__)
50
+ base_spec = Gem::Specification.load(File.join(gem_root, "omnizip.gemspec"))
51
+ abort "omnizip.gemspec failed to load" unless base_spec
52
+ base_files = base_spec.files.select { |rel| File.file?(File.join(gem_root, rel)) }.freeze
53
+
45
54
  Dir.glob(File.join(dir, "libomnizip_ffi-*.tar.gz")).each do |tarball|
46
55
  target = File.basename(tarball, ".tar.gz").delete_prefix("libomnizip_ffi-")
47
56
  platform = PLATFORM_GEM_TARGETS[target] or abort "no rubygems platform mapping for #{target}"
48
57
  require "tmpdir"
49
58
  Dir.mktmpdir do |tmp|
50
59
  binary = extract_binary(tarball, tmp)
51
- out = build_platform_gem(platform, binary)
60
+ out = build_platform_gem(platform, binary, base_files)
52
61
  puts "built: #{out} (#{File.size(out)} bytes)"
53
62
  end
54
63
  end
55
64
  end
56
65
 
66
+ desc "Push every already-built platform gem under pkg/ to rubygems (idempotent)"
67
+ task :release do
68
+ require "rake"
69
+ require "rubygems/package"
70
+ gem_root = File.expand_path("../..", __dir__)
71
+ pkg = File.join(gem_root, "pkg")
72
+ gems = Dir.glob(File.join(pkg, "omnizip-*.gem"))
73
+ abort "no platform gems under #{pkg} — run platform_gems:build first" if gems.empty?
74
+
75
+ # Pushes only what's already built in pkg/. Invoked by the
76
+ # platform-gem push job, which dispatches the trusted
77
+ # release.yml with next_version=skip + release_command override
78
+ # so the cimas reusable workflow authenticates via the
79
+ # already-trusted OIDC configuration and runs this task verbatim.
80
+ #
81
+ # Idempotent against re-push of an already-published version
82
+ # (cimas reusable handles "already been pushed" via the
83
+ # gem-idempotent-push-guard action).
84
+ gems.each do |path|
85
+ puts "pushing #{File.basename(path)}"
86
+ run_gem_push(path)
87
+ end
88
+ end
89
+
57
90
  def extract_binary(tarball, tmp)
58
91
  system("tar", "-xzf", tarball, "-C", tmp) or abort "tar failed for #{tarball}"
59
92
  Dir.glob(File.join(tmp, "*")).first or abort "empty tarball #{tarball}"
60
93
  end
61
94
 
95
+ def run_gem_push(path)
96
+ require "rubygems/command/push_command"
97
+ cmd = Gem::Command::PushCommand.new
98
+ cmd.invoke("push", path)
99
+ rescue StandardError => e
100
+ raise if !/already been pushed|Repushing of gem versions is not allowed/i.match?(e.message)
101
+
102
+ warn "skip (already published): #{File.basename(path)}"
103
+ end
104
+
62
105
  # Stage the tracked gem files plus the vendored cdylib, flip the
63
- # gemspec platform, and build.
64
- def build_platform_gem(platform, dylib)
106
+ # gemspec platform, and build. `base_files` is the pristine
107
+ # gemspec file list (frozen); we ASSIGN a fresh list every call
108
+ # rather than mutating via +=, because Gem::Specification.load
109
+ # hands back a cached object.
110
+ def build_platform_gem(platform, dylib, base_files = nil)
65
111
  require "fileutils"
66
112
  require "rubygems/package"
67
113
  require "tmpdir"
@@ -69,31 +115,25 @@ namespace :platform_gems do
69
115
  gem_root = File.expand_path("../..", __dir__)
70
116
  spec = Gem::Specification.load(File.join(gem_root, "omnizip.gemspec"))
71
117
  abort "omnizip.gemspec failed to load" unless spec
118
+ base_files ||= spec.files.select { |rel| File.file?(File.join(gem_root, rel)) }
72
119
 
73
120
  Dir.mktmpdir do |tmp|
74
121
  stage = File.join(tmp, "stage")
75
- spec.files.each do |rel|
76
- src = File.join(gem_root, rel)
77
- next unless File.file?(src)
78
-
122
+ base_files.each do |rel|
79
123
  dst = File.join(stage, rel)
80
124
  FileUtils.mkdir_p(File.dirname(dst))
81
- FileUtils.cp(src, dst)
125
+ FileUtils.cp(File.join(gem_root, rel), dst)
82
126
  end
83
127
 
84
128
  vendored = "vendor/libomnizip_ffi#{File.extname(dylib)}"
85
129
  FileUtils.mkdir_p(File.join(stage, "vendor"))
86
130
  FileUtils.cp(dylib, File.join(stage, vendored))
131
+ abort "staged cdylib missing: #{vendored}" unless File.file?(File.join(stage, vendored))
87
132
 
88
133
  spec.platform = Gem::Platform.new(platform)
89
- spec.files += [vendored]
134
+ spec.files = base_files + [vendored]
90
135
  spec.test_files = []
91
136
 
92
- gemspec_path = File.join(stage, "omnizip.gemspec")
93
- File.write(gemspec_path, spec.to_ruby)
94
-
95
- pkg = File.join(tmp, "pkg")
96
- FileUtils.mkdir_p(pkg)
97
137
  Dir.chdir(stage) do
98
138
  Gem::Package.build(spec)
99
139
  end
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.4.7
4
+ version: 0.4.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.