Module: RSpecTracer::RemoteCache::Archive Private
- Defined in:
- lib/rspec_tracer/remote_cache/archive.rb
Overview
This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.
tar+gzip pack/extract for the remote-cache S3 payload. Pure
Ruby stdlib (rubygems/package + zlib) - no shell-out, no
new gem deps, works on every supported interpreter (MRI 3.1+
and JRuby 9.4 both bundle both modules).
Wire format: single .tar.gz containing last_run.json at the
archive root plus a <run_id>/ directory with the 15 JSON
files. Replaces the 1.x per-file layout on S3 (N+1 objects per
cache -> 1 object), shrinks payload ~4-6x via gzip on the highly-
redundant JSON (shared example IDs across files), and cuts the
per-download/upload request count from 15+ to exactly 2
(cp for download, cp for upload).
Local cache_path layout is UNCHANGED - the archive is pack/unpack
boundary for transit only. User-facing filenames in
USER_FACING_SURFACE.md section 6 stay as documented; external tooling
that walks rspec_tracer_cache/ sees the same 15-file layout.
Constant Summary collapse
- CACHE_FILENAME =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Internal constant.
'cache.tar.gz'
Class Method Summary collapse
-
.extract(archive_path:, dest_dir:) ⇒ Object
private
Extract
archive_pathintodest_dir. -
.pack(cache_path:, run_id:, dest_path:) ⇒ Object
private
Pack the relevant contents of
cache_pathintodest_path. -
.safe_entry_name(name) ⇒ Object
private
Refuse absolute paths or
..traversal.
Class Method Details
.extract(archive_path:, dest_dir:) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Extract archive_path into dest_dir. Overwrites existing
files (run-dir already present gets replaced). Raises on a
malformed archive; caller rescues.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/rspec_tracer/remote_cache/archive.rb', line 80 def self.extract(archive_path:, dest_dir:) raise ArgumentError, 'archive_path is required' if archive_path.nil? || archive_path.empty? raise ArgumentError, 'dest_dir is required' if dest_dir.nil? || dest_dir.empty? raise ArgumentError, "missing archive at #{archive_path}" unless File.file?(archive_path) FileUtils.mkdir_p(dest_dir) File.open(archive_path, 'rb') do |file| Zlib::GzipReader.wrap(file) do |gz| Gem::Package::TarReader.new(gz) do |tar| tar.each { |entry| write_entry(entry, dest_dir) } end end end dest_dir end |
.pack(cache_path:, run_id:, dest_path:) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Pack the relevant contents of cache_path into dest_path.
Required: cache_path/last_run.json and cache_path/<run_id>/.
Raises ArgumentError on a malformed cache; any I/O error during
pack propagates to the caller (S3Backend wraps in S3BackendError
- rescues at the orchestrator boundary for graceful degradation).
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/rspec_tracer/remote_cache/archive.rb', line 38 def self.pack(cache_path:, run_id:, dest_path:) validate_pack_args!(cache_path, run_id, dest_path) last_run, run_dir = resolve_pack_sources!(cache_path, run_id) File.open(dest_path, 'wb') do |file| Zlib::GzipWriter.wrap(file) do |gz| Gem::Package::TarWriter.new(gz) do |tar| add_file(tar, last_run, 'last_run.json') Dir[File.join(run_dir, '*.json')].each do |path| add_file(tar, path, File.join(run_id, File.basename(path))) end end end end dest_path end |
.safe_entry_name(name) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Refuse absolute paths or .. traversal. Both are illegal in a
well-formed cache entry name; silently dropping them beats
trusting a remote-sourced value to write anywhere on disk.
Public because RedisBackend reuses the same guard on hash field
names (the Redis equivalent of tar entry names).
128 129 130 131 132 133 134 |
# File 'lib/rspec_tracer/remote_cache/archive.rb', line 128 def self.safe_entry_name(name) return nil if name.nil? || name.empty? return nil if name.start_with?('/') return nil if name.split('/').include?('..') name end |