implement cross-platform tar extraction utility with symlink resolution fallback for setup scripts

This commit is contained in:
jarves committed 2026-06-26 10:09:38 +05:30
1 parent 349b9e167f
commit 690cce5cc3
3 files changed
+134 -2

No files matched your search

+124
View File
@@ -0,0 +1,124 @@
#!/usr/bin/env python3
import os
import sys
import shutil
import tarfile
import argparse
def main():
parser = argparse.ArgumentParser(description="Extract tar archives, resolving symlinks as copies if symlinks are not supported.")
parser.add_argument("--archive", required=True, help="Path to the tar archive")
parser.add_argument("--dest", required=True, help="Destination directory")
parser.add_argument("--strip-components", type=int, default=0, help="Number of leading components to strip")
args = parser.parse_args()
archive_path = args.archive
dest_dir = args.dest
strip_components = args.strip_components
if not os.path.exists(archive_path):
print(f"Error: Archive not found: {archive_path}", file=sys.stderr)
sys.exit(1)
os.makedirs(dest_dir, exist_ok=True)
# Check if filesystem supports symlinks in dest_dir
test_link = os.path.join(dest_dir, f".test_symlink_{os.getpid()}")
use_symlinks = True
try:
if os.path.lexists(test_link):
os.unlink(test_link)
os.symlink("test_target", test_link)
os.unlink(test_link)
except OSError:
use_symlinks = False
print(f"Extracting {archive_path} to {dest_dir} (strip-components={strip_components}, use_symlinks={use_symlinks})...")
try:
with tarfile.open(archive_path, "r") as tar:
members = tar.getmembers()
# Map of stripped_path -> target (for symlinks)
symlinks_dict = {}
regular_members = []
for member in members:
parts = member.name.split('/')
# Strip leading components
if len(parts) <= strip_components:
continue
stripped_name = '/'.join(parts[strip_components:])
if not stripped_name:
continue
# Update the member name in-place
member.name = stripped_name
if member.issym() or member.islnk():
symlinks_dict[member.name] = member.linkname
else:
regular_members.append(member)
# Extract regular files and directories first
tar.extractall(path=dest_dir, members=regular_members)
# Now handle symlinks
for link_path, link_target in symlinks_dict.items():
full_link_path = os.path.join(dest_dir, link_path)
# Resolve final target recursively in case of nested symlinks
link_dir = os.path.dirname(full_link_path)
# Recursive resolver helper
def resolve_final_target(current_path, current_target):
# target_abs is the absolute path to the target file/dir
target_abs = os.path.normpath(os.path.join(os.path.dirname(current_path), current_target))
# Check if target_abs is inside dest_dir
rel_to_dest = os.path.relpath(target_abs, dest_dir)
if rel_to_dest in symlinks_dict:
next_target = symlinks_dict[rel_to_dest]
return resolve_final_target(target_abs, next_target)
return target_abs
target_abs = resolve_final_target(full_link_path, link_target)
# Ensure parent directory exists
os.makedirs(link_dir, exist_ok=True)
if os.path.lexists(full_link_path):
try:
os.unlink(full_link_path)
except OSError:
shutil.rmtree(full_link_path, ignore_errors=True)
if use_symlinks:
try:
os.symlink(link_target, full_link_path)
except OSError:
# Fallback to copy if symlink creation fails unexpectedly
copy_fallback(target_abs, full_link_path)
else:
copy_fallback(target_abs, full_link_path)
print("Extraction completed successfully.")
except Exception as e:
print(f"Error extracting archive: {e}", file=sys.stderr)
sys.exit(1)
def copy_fallback(src, dst):
if not os.path.exists(src):
# Target doesn't exist. Maybe it's an external link or wasn't extracted.
print(f"Warning: Symlink target does not exist, skipping copy: {src}", file=sys.stderr)
return
try:
if os.path.isdir(src):
shutil.copytree(src, dst)
else:
shutil.copy2(src, dst)
print(f"Created fallback copy: {dst} -> {src}")
except Exception as e:
print(f"Error copying {src} to {dst}: {e}", file=sys.stderr)
if __name__ == "__main__":
main()
+5 -1
View File
@@ -25,7 +25,11 @@ download_and_extract() {
echo " >> Downloading $asset"
curl -fSL --progress-bar "$url" -o "$archive.part"
mv "$archive.part" "$archive"
tar -xzf "$archive" -C "$dest" --strip-components=1
if command -v python3 >/dev/null 2>&1; then
python3 "$SCRIPT_DIR/extract_tar.py" --archive "$archive" --dest "$dest" --strip-components=1
else
tar -xzf "$archive" -C "$dest" --strip-components=1
fi
rm -f "$archive"
chmod +x "$dest"/llama-* 2>/dev/null || true
+5 -1
View File
@@ -25,7 +25,11 @@ download_and_extract() {
echo " >> Downloading $asset"
curl -fSL --progress-bar "$url" -o "$archive.part"
mv "$archive.part" "$archive"
tar -xzf "$archive" -C "$dest" --strip-components=1
if command -v python3 >/dev/null 2>&1; then
python3 "$SCRIPT_DIR/extract_tar.py" --archive "$archive" --dest "$dest" --strip-components=1
else
tar -xzf "$archive" -C "$dest" --strip-components=1
fi
rm -f "$archive"
chmod +x "$dest"/whisper-* "$dest"/main "$dest"/server 2>/dev/null || true